18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Renesas R-Car DVC support 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Copyright (C) 2014 Renesas Solutions Corp. 68c2ecf20Sopenharmony_ci// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci/* 98c2ecf20Sopenharmony_ci * Playback Volume 108c2ecf20Sopenharmony_ci * amixer set "DVC Out" 100% 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Capture Volume 138c2ecf20Sopenharmony_ci * amixer set "DVC In" 100% 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Playback Mute 168c2ecf20Sopenharmony_ci * amixer set "DVC Out Mute" on 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * Capture Mute 198c2ecf20Sopenharmony_ci * amixer set "DVC In Mute" on 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * Volume Ramp 228c2ecf20Sopenharmony_ci * amixer set "DVC Out Ramp Up Rate" "0.125 dB/64 steps" 238c2ecf20Sopenharmony_ci * amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps" 248c2ecf20Sopenharmony_ci * amixer set "DVC Out Ramp" on 258c2ecf20Sopenharmony_ci * aplay xxx.wav & 268c2ecf20Sopenharmony_ci * amixer set "DVC Out" 80% // Volume Down 278c2ecf20Sopenharmony_ci * amixer set "DVC Out" 100% // Volume Up 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "rsnd.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define RSND_DVC_NAME_SIZE 16 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define DVC_NAME "dvc" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct rsnd_dvc { 378c2ecf20Sopenharmony_ci struct rsnd_mod mod; 388c2ecf20Sopenharmony_ci struct rsnd_kctrl_cfg_m volume; 398c2ecf20Sopenharmony_ci struct rsnd_kctrl_cfg_m mute; 408c2ecf20Sopenharmony_ci struct rsnd_kctrl_cfg_s ren; /* Ramp Enable */ 418c2ecf20Sopenharmony_ci struct rsnd_kctrl_cfg_s rup; /* Ramp Rate Up */ 428c2ecf20Sopenharmony_ci struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id) 468c2ecf20Sopenharmony_ci#define rsnd_dvc_nr(priv) ((priv)->dvc_nr) 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define rsnd_mod_to_dvc(_mod) \ 498c2ecf20Sopenharmony_ci container_of((_mod), struct rsnd_dvc, mod) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define for_each_rsnd_dvc(pos, priv, i) \ 528c2ecf20Sopenharmony_ci for ((i) = 0; \ 538c2ecf20Sopenharmony_ci ((i) < rsnd_dvc_nr(priv)) && \ 548c2ecf20Sopenharmony_ci ((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \ 558c2ecf20Sopenharmony_ci i++) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void rsnd_dvc_activation(struct rsnd_mod *mod) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_SWRSR, 0); 608c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_SWRSR, 1); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic void rsnd_dvc_halt(struct rsnd_mod *mod) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_DVUIR, 1); 668c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_SWRSR, 0); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define rsnd_dvc_get_vrpdr(dvc) (rsnd_kctrl_vals(dvc->rup) << 8 | \ 708c2ecf20Sopenharmony_ci rsnd_kctrl_vals(dvc->rdown)) 718c2ecf20Sopenharmony_ci#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (rsnd_kctrl_valm(dvc->volume, 0) >> 13)) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io, 748c2ecf20Sopenharmony_ci struct rsnd_mod *mod) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); 778c2ecf20Sopenharmony_ci u32 val[RSND_MAX_CHANNELS]; 788c2ecf20Sopenharmony_ci int i; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* Enable Ramp */ 818c2ecf20Sopenharmony_ci if (rsnd_kctrl_vals(dvc->ren)) 828c2ecf20Sopenharmony_ci for (i = 0; i < RSND_MAX_CHANNELS; i++) 838c2ecf20Sopenharmony_ci val[i] = rsnd_kctrl_max(dvc->volume); 848c2ecf20Sopenharmony_ci else 858c2ecf20Sopenharmony_ci for (i = 0; i < RSND_MAX_CHANNELS; i++) 868c2ecf20Sopenharmony_ci val[i] = rsnd_kctrl_valm(dvc->volume, i); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* Enable Digital Volume */ 898c2ecf20Sopenharmony_ci for (i = 0; i < RSND_MAX_CHANNELS; i++) 908c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_VOLxR(i), val[i]); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void rsnd_dvc_volume_init(struct rsnd_dai_stream *io, 948c2ecf20Sopenharmony_ci struct rsnd_mod *mod) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); 978c2ecf20Sopenharmony_ci u32 adinr = 0; 988c2ecf20Sopenharmony_ci u32 dvucr = 0; 998c2ecf20Sopenharmony_ci u32 vrctr = 0; 1008c2ecf20Sopenharmony_ci u32 vrpdr = 0; 1018c2ecf20Sopenharmony_ci u32 vrdbr = 0; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci adinr = rsnd_get_adinr_bit(mod, io) | 1048c2ecf20Sopenharmony_ci rsnd_runtime_channel_after_ctu(io); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* Enable Digital Volume, Zero Cross Mute Mode */ 1078c2ecf20Sopenharmony_ci dvucr |= 0x101; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Enable Ramp */ 1108c2ecf20Sopenharmony_ci if (rsnd_kctrl_vals(dvc->ren)) { 1118c2ecf20Sopenharmony_ci dvucr |= 0x10; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* 1148c2ecf20Sopenharmony_ci * FIXME !! 1158c2ecf20Sopenharmony_ci * use scale-downed Digital Volume 1168c2ecf20Sopenharmony_ci * as Volume Ramp 1178c2ecf20Sopenharmony_ci * 7F FFFF -> 3FF 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci vrctr = 0xff; 1208c2ecf20Sopenharmony_ci vrpdr = rsnd_dvc_get_vrpdr(dvc); 1218c2ecf20Sopenharmony_ci vrdbr = rsnd_dvc_get_vrdbr(dvc); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Initialize operation */ 1258c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_DVUIR, 1); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* General Information */ 1288c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_ADINR, adinr); 1298c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_DVUCR, dvucr); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Volume Ramp Parameter */ 1328c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_VRCTR, vrctr); 1338c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_VRPDR, vrpdr); 1348c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_VRDBR, vrdbr); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* Digital Volume Function Parameter */ 1378c2ecf20Sopenharmony_ci rsnd_dvc_volume_parameter(io, mod); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* cancel operation */ 1408c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_DVUIR, 0); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void rsnd_dvc_volume_update(struct rsnd_dai_stream *io, 1448c2ecf20Sopenharmony_ci struct rsnd_mod *mod) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); 1478c2ecf20Sopenharmony_ci u32 zcmcr = 0; 1488c2ecf20Sopenharmony_ci u32 vrpdr = 0; 1498c2ecf20Sopenharmony_ci u32 vrdbr = 0; 1508c2ecf20Sopenharmony_ci int i; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci for (i = 0; i < rsnd_kctrl_size(dvc->mute); i++) 1538c2ecf20Sopenharmony_ci zcmcr |= (!!rsnd_kctrl_valm(dvc->mute, i)) << i; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (rsnd_kctrl_vals(dvc->ren)) { 1568c2ecf20Sopenharmony_ci vrpdr = rsnd_dvc_get_vrpdr(dvc); 1578c2ecf20Sopenharmony_ci vrdbr = rsnd_dvc_get_vrdbr(dvc); 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* Disable DVC Register access */ 1618c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_DVUER, 0); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Zero Cross Mute Function */ 1648c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_ZCMCR, zcmcr); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* Volume Ramp Function */ 1678c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_VRPDR, vrpdr); 1688c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_VRDBR, vrdbr); 1698c2ecf20Sopenharmony_ci /* add DVC_VRWTR here */ 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Digital Volume Function Parameter */ 1728c2ecf20Sopenharmony_ci rsnd_dvc_volume_parameter(io, mod); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* Enable DVC Register access */ 1758c2ecf20Sopenharmony_ci rsnd_mod_write(mod, DVC_DVUER, 1); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int rsnd_dvc_probe_(struct rsnd_mod *mod, 1798c2ecf20Sopenharmony_ci struct rsnd_dai_stream *io, 1808c2ecf20Sopenharmony_ci struct rsnd_priv *priv) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci return rsnd_cmd_attach(io, rsnd_mod_id(mod)); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int rsnd_dvc_init(struct rsnd_mod *mod, 1868c2ecf20Sopenharmony_ci struct rsnd_dai_stream *io, 1878c2ecf20Sopenharmony_ci struct rsnd_priv *priv) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci int ret; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci ret = rsnd_mod_power_on(mod); 1928c2ecf20Sopenharmony_ci if (ret < 0) 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci rsnd_dvc_activation(mod); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci rsnd_dvc_volume_init(io, mod); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci rsnd_dvc_volume_update(io, mod); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return 0; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int rsnd_dvc_quit(struct rsnd_mod *mod, 2058c2ecf20Sopenharmony_ci struct rsnd_dai_stream *io, 2068c2ecf20Sopenharmony_ci struct rsnd_priv *priv) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci rsnd_dvc_halt(mod); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci rsnd_mod_power_off(mod); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return 0; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int rsnd_dvc_pcm_new(struct rsnd_mod *mod, 2168c2ecf20Sopenharmony_ci struct rsnd_dai_stream *io, 2178c2ecf20Sopenharmony_ci struct snd_soc_pcm_runtime *rtd) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod); 2208c2ecf20Sopenharmony_ci struct rsnd_dai *rdai = rsnd_io_to_rdai(io); 2218c2ecf20Sopenharmony_ci int is_play = rsnd_io_is_play(io); 2228c2ecf20Sopenharmony_ci int channels = rsnd_rdai_channels_get(rdai); 2238c2ecf20Sopenharmony_ci int ret; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* Volume */ 2268c2ecf20Sopenharmony_ci ret = rsnd_kctrl_new_m(mod, io, rtd, 2278c2ecf20Sopenharmony_ci is_play ? 2288c2ecf20Sopenharmony_ci "DVC Out Playback Volume" : "DVC In Capture Volume", 2298c2ecf20Sopenharmony_ci rsnd_kctrl_accept_anytime, 2308c2ecf20Sopenharmony_ci rsnd_dvc_volume_update, 2318c2ecf20Sopenharmony_ci &dvc->volume, channels, 2328c2ecf20Sopenharmony_ci 0x00800000 - 1); 2338c2ecf20Sopenharmony_ci if (ret < 0) 2348c2ecf20Sopenharmony_ci return ret; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* Mute */ 2378c2ecf20Sopenharmony_ci ret = rsnd_kctrl_new_m(mod, io, rtd, 2388c2ecf20Sopenharmony_ci is_play ? 2398c2ecf20Sopenharmony_ci "DVC Out Mute Switch" : "DVC In Mute Switch", 2408c2ecf20Sopenharmony_ci rsnd_kctrl_accept_anytime, 2418c2ecf20Sopenharmony_ci rsnd_dvc_volume_update, 2428c2ecf20Sopenharmony_ci &dvc->mute, channels, 2438c2ecf20Sopenharmony_ci 1); 2448c2ecf20Sopenharmony_ci if (ret < 0) 2458c2ecf20Sopenharmony_ci return ret; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* Ramp */ 2488c2ecf20Sopenharmony_ci ret = rsnd_kctrl_new_s(mod, io, rtd, 2498c2ecf20Sopenharmony_ci is_play ? 2508c2ecf20Sopenharmony_ci "DVC Out Ramp Switch" : "DVC In Ramp Switch", 2518c2ecf20Sopenharmony_ci rsnd_kctrl_accept_anytime, 2528c2ecf20Sopenharmony_ci rsnd_dvc_volume_update, 2538c2ecf20Sopenharmony_ci &dvc->ren, 1); 2548c2ecf20Sopenharmony_ci if (ret < 0) 2558c2ecf20Sopenharmony_ci return ret; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci ret = rsnd_kctrl_new_e(mod, io, rtd, 2588c2ecf20Sopenharmony_ci is_play ? 2598c2ecf20Sopenharmony_ci "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate", 2608c2ecf20Sopenharmony_ci rsnd_kctrl_accept_anytime, 2618c2ecf20Sopenharmony_ci rsnd_dvc_volume_update, 2628c2ecf20Sopenharmony_ci &dvc->rup, 2638c2ecf20Sopenharmony_ci volume_ramp_rate, 2648c2ecf20Sopenharmony_ci VOLUME_RAMP_MAX_DVC); 2658c2ecf20Sopenharmony_ci if (ret < 0) 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci ret = rsnd_kctrl_new_e(mod, io, rtd, 2698c2ecf20Sopenharmony_ci is_play ? 2708c2ecf20Sopenharmony_ci "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate", 2718c2ecf20Sopenharmony_ci rsnd_kctrl_accept_anytime, 2728c2ecf20Sopenharmony_ci rsnd_dvc_volume_update, 2738c2ecf20Sopenharmony_ci &dvc->rdown, 2748c2ecf20Sopenharmony_ci volume_ramp_rate, 2758c2ecf20Sopenharmony_ci VOLUME_RAMP_MAX_DVC); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (ret < 0) 2788c2ecf20Sopenharmony_ci return ret; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io, 2848c2ecf20Sopenharmony_ci struct rsnd_mod *mod) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct rsnd_priv *priv = rsnd_mod_to_priv(mod); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return rsnd_dma_request_channel(rsnd_dvc_of_node(priv), 2898c2ecf20Sopenharmony_ci mod, "tx"); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic struct rsnd_mod_ops rsnd_dvc_ops = { 2938c2ecf20Sopenharmony_ci .name = DVC_NAME, 2948c2ecf20Sopenharmony_ci .dma_req = rsnd_dvc_dma_req, 2958c2ecf20Sopenharmony_ci .probe = rsnd_dvc_probe_, 2968c2ecf20Sopenharmony_ci .init = rsnd_dvc_init, 2978c2ecf20Sopenharmony_ci .quit = rsnd_dvc_quit, 2988c2ecf20Sopenharmony_ci .pcm_new = rsnd_dvc_pcm_new, 2998c2ecf20Sopenharmony_ci .get_status = rsnd_mod_get_status, 3008c2ecf20Sopenharmony_ci}; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistruct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv))) 3058c2ecf20Sopenharmony_ci id = 0; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci return rsnd_mod_get(rsnd_dvc_get(priv, id)); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ciint rsnd_dvc_probe(struct rsnd_priv *priv) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct device_node *node; 3138c2ecf20Sopenharmony_ci struct device_node *np; 3148c2ecf20Sopenharmony_ci struct device *dev = rsnd_priv_to_dev(priv); 3158c2ecf20Sopenharmony_ci struct rsnd_dvc *dvc; 3168c2ecf20Sopenharmony_ci struct clk *clk; 3178c2ecf20Sopenharmony_ci char name[RSND_DVC_NAME_SIZE]; 3188c2ecf20Sopenharmony_ci int i, nr, ret; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* This driver doesn't support Gen1 at this point */ 3218c2ecf20Sopenharmony_ci if (rsnd_is_gen1(priv)) 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci node = rsnd_dvc_of_node(priv); 3258c2ecf20Sopenharmony_ci if (!node) 3268c2ecf20Sopenharmony_ci return 0; /* not used is not error */ 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci nr = of_get_child_count(node); 3298c2ecf20Sopenharmony_ci if (!nr) { 3308c2ecf20Sopenharmony_ci ret = -EINVAL; 3318c2ecf20Sopenharmony_ci goto rsnd_dvc_probe_done; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci dvc = devm_kcalloc(dev, nr, sizeof(*dvc), GFP_KERNEL); 3358c2ecf20Sopenharmony_ci if (!dvc) { 3368c2ecf20Sopenharmony_ci ret = -ENOMEM; 3378c2ecf20Sopenharmony_ci goto rsnd_dvc_probe_done; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci priv->dvc_nr = nr; 3418c2ecf20Sopenharmony_ci priv->dvc = dvc; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci i = 0; 3448c2ecf20Sopenharmony_ci ret = 0; 3458c2ecf20Sopenharmony_ci for_each_child_of_node(node, np) { 3468c2ecf20Sopenharmony_ci dvc = rsnd_dvc_get(priv, i); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d", 3498c2ecf20Sopenharmony_ci DVC_NAME, i); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci clk = devm_clk_get(dev, name); 3528c2ecf20Sopenharmony_ci if (IS_ERR(clk)) { 3538c2ecf20Sopenharmony_ci ret = PTR_ERR(clk); 3548c2ecf20Sopenharmony_ci of_node_put(np); 3558c2ecf20Sopenharmony_ci goto rsnd_dvc_probe_done; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, 3598c2ecf20Sopenharmony_ci clk, RSND_MOD_DVC, i); 3608c2ecf20Sopenharmony_ci if (ret) { 3618c2ecf20Sopenharmony_ci of_node_put(np); 3628c2ecf20Sopenharmony_ci goto rsnd_dvc_probe_done; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci i++; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cirsnd_dvc_probe_done: 3698c2ecf20Sopenharmony_ci of_node_put(node); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return ret; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_civoid rsnd_dvc_remove(struct rsnd_priv *priv) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct rsnd_dvc *dvc; 3778c2ecf20Sopenharmony_ci int i; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci for_each_rsnd_dvc(dvc, priv, i) { 3808c2ecf20Sopenharmony_ci rsnd_mod_quit(rsnd_mod_get(dvc)); 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci} 383