18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Asihpi soundcard 48c2ecf20Sopenharmony_ci * Copyright (c) by AudioScience Inc <support@audioscience.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * The following is not a condition of use, merely a request: 78c2ecf20Sopenharmony_ci * If you modify this program, particularly if you fix errors, AudioScience Inc 88c2ecf20Sopenharmony_ci * would appreciate it if you grant us the right to use those modifications 98c2ecf20Sopenharmony_ci * for any purpose including commercial applications. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "hpi_internal.h" 138c2ecf20Sopenharmony_ci#include "hpi_version.h" 148c2ecf20Sopenharmony_ci#include "hpimsginit.h" 158c2ecf20Sopenharmony_ci#include "hpioctl.h" 168c2ecf20Sopenharmony_ci#include "hpicmn.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <linux/init.h> 208c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/time.h> 238c2ecf20Sopenharmony_ci#include <linux/wait.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <sound/core.h> 268c2ecf20Sopenharmony_ci#include <sound/control.h> 278c2ecf20Sopenharmony_ci#include <sound/pcm.h> 288c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 298c2ecf20Sopenharmony_ci#include <sound/info.h> 308c2ecf20Sopenharmony_ci#include <sound/initval.h> 318c2ecf20Sopenharmony_ci#include <sound/tlv.h> 328c2ecf20Sopenharmony_ci#include <sound/hwdep.h> 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 358c2ecf20Sopenharmony_ciMODULE_AUTHOR("AudioScience inc. <support@audioscience.com>"); 368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AudioScience ALSA ASI5xxx ASI6xxx ASI87xx ASI89xx " 378c2ecf20Sopenharmony_ci HPI_VER_STRING); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#if defined CONFIG_SND_DEBUG_VERBOSE 408c2ecf20Sopenharmony_ci/** 418c2ecf20Sopenharmony_ci * snd_printddd - very verbose debug printk 428c2ecf20Sopenharmony_ci * @format: format string 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * Works like snd_printk() for debugging purposes. 458c2ecf20Sopenharmony_ci * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set. 468c2ecf20Sopenharmony_ci * Must set snd module debug parameter to 3 to enable at runtime. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_ci#define snd_printddd(format, args...) \ 498c2ecf20Sopenharmony_ci __snd_printk(3, __FILE__, __LINE__, format, ##args) 508c2ecf20Sopenharmony_ci#else 518c2ecf20Sopenharmony_ci#define snd_printddd(format, args...) do { } while (0) 528c2ecf20Sopenharmony_ci#endif 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */ 558c2ecf20Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 568c2ecf20Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 578c2ecf20Sopenharmony_cistatic bool enable_hpi_hwdep = 1; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(index, "ALSA index value for AudioScience soundcard."); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 638c2ecf20Sopenharmony_ciMODULE_PARM_DESC(id, "ALSA ID string for AudioScience soundcard."); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable, "ALSA enable AudioScience soundcard."); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cimodule_param(enable_hpi_hwdep, bool, 0644); 698c2ecf20Sopenharmony_ciMODULE_PARM_DESC(enable_hpi_hwdep, 708c2ecf20Sopenharmony_ci "ALSA enable HPI hwdep for AudioScience soundcard "); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* identify driver */ 738c2ecf20Sopenharmony_ci#ifdef KERNEL_ALSA_BUILD 748c2ecf20Sopenharmony_cistatic char *build_info = "Built using headers from kernel source"; 758c2ecf20Sopenharmony_cimodule_param(build_info, charp, 0444); 768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(build_info, "Built using headers from kernel source"); 778c2ecf20Sopenharmony_ci#else 788c2ecf20Sopenharmony_cistatic char *build_info = "Built within ALSA source"; 798c2ecf20Sopenharmony_cimodule_param(build_info, charp, 0444); 808c2ecf20Sopenharmony_ciMODULE_PARM_DESC(build_info, "Built within ALSA source"); 818c2ecf20Sopenharmony_ci#endif 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* set to 1 to dump every control from adapter to log */ 848c2ecf20Sopenharmony_cistatic const int mixer_dump; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define DEFAULT_SAMPLERATE 44100 878c2ecf20Sopenharmony_cistatic int adapter_fs = DEFAULT_SAMPLERATE; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* defaults */ 908c2ecf20Sopenharmony_ci#define PERIODS_MIN 2 918c2ecf20Sopenharmony_ci#define PERIOD_BYTES_MIN 2048 928c2ecf20Sopenharmony_ci#define BUFFER_BYTES_MAX (512 * 1024) 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci#define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7) 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistruct clk_source { 978c2ecf20Sopenharmony_ci int source; 988c2ecf20Sopenharmony_ci int index; 998c2ecf20Sopenharmony_ci const char *name; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistruct clk_cache { 1038c2ecf20Sopenharmony_ci int count; 1048c2ecf20Sopenharmony_ci int has_local; 1058c2ecf20Sopenharmony_ci struct clk_source s[MAX_CLOCKSOURCES]; 1068c2ecf20Sopenharmony_ci}; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* Per card data */ 1098c2ecf20Sopenharmony_cistruct snd_card_asihpi { 1108c2ecf20Sopenharmony_ci struct snd_card *card; 1118c2ecf20Sopenharmony_ci struct pci_dev *pci; 1128c2ecf20Sopenharmony_ci struct hpi_adapter *hpi; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* In low latency mode there is only one stream, a pointer to its 1158c2ecf20Sopenharmony_ci * private data is stored here on trigger and cleared on stop. 1168c2ecf20Sopenharmony_ci * The interrupt handler uses it as a parameter when calling 1178c2ecf20Sopenharmony_ci * snd_card_asihpi_timer_function(). 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *llmode_streampriv; 1208c2ecf20Sopenharmony_ci void (*pcm_start)(struct snd_pcm_substream *substream); 1218c2ecf20Sopenharmony_ci void (*pcm_stop)(struct snd_pcm_substream *substream); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci u32 h_mixer; 1248c2ecf20Sopenharmony_ci struct clk_cache cc; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci u16 can_dma; 1278c2ecf20Sopenharmony_ci u16 support_grouping; 1288c2ecf20Sopenharmony_ci u16 support_mrx; 1298c2ecf20Sopenharmony_ci u16 update_interval_frames; 1308c2ecf20Sopenharmony_ci u16 in_max_chans; 1318c2ecf20Sopenharmony_ci u16 out_max_chans; 1328c2ecf20Sopenharmony_ci u16 in_min_chans; 1338c2ecf20Sopenharmony_ci u16 out_min_chans; 1348c2ecf20Sopenharmony_ci}; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci/* Per stream data */ 1378c2ecf20Sopenharmony_cistruct snd_card_asihpi_pcm { 1388c2ecf20Sopenharmony_ci struct timer_list timer; 1398c2ecf20Sopenharmony_ci unsigned int respawn_timer; 1408c2ecf20Sopenharmony_ci unsigned int hpi_buffer_attached; 1418c2ecf20Sopenharmony_ci unsigned int buffer_bytes; 1428c2ecf20Sopenharmony_ci unsigned int period_bytes; 1438c2ecf20Sopenharmony_ci unsigned int bytes_per_sec; 1448c2ecf20Sopenharmony_ci unsigned int pcm_buf_host_rw_ofs; /* Host R/W pos */ 1458c2ecf20Sopenharmony_ci unsigned int pcm_buf_dma_ofs; /* DMA R/W offset in buffer */ 1468c2ecf20Sopenharmony_ci unsigned int pcm_buf_elapsed_dma_ofs; /* DMA R/W offset in buffer */ 1478c2ecf20Sopenharmony_ci unsigned int drained_count; 1488c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 1498c2ecf20Sopenharmony_ci u32 h_stream; 1508c2ecf20Sopenharmony_ci struct hpi_format format; 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* universal stream verbs work with out or in stream handles */ 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* Functions to allow driver to give a buffer to HPI for busmastering */ 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic u16 hpi_stream_host_buffer_attach( 1588c2ecf20Sopenharmony_ci u32 h_stream, /* handle to outstream. */ 1598c2ecf20Sopenharmony_ci u32 size_in_bytes, /* size in bytes of bus mastering buffer */ 1608c2ecf20Sopenharmony_ci u32 pci_address 1618c2ecf20Sopenharmony_ci) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct hpi_message hm; 1648c2ecf20Sopenharmony_ci struct hpi_response hr; 1658c2ecf20Sopenharmony_ci unsigned int obj = hpi_handle_object(h_stream); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!h_stream) 1688c2ecf20Sopenharmony_ci return HPI_ERROR_INVALID_OBJ; 1698c2ecf20Sopenharmony_ci hpi_init_message_response(&hm, &hr, obj, 1708c2ecf20Sopenharmony_ci obj == HPI_OBJ_OSTREAM ? 1718c2ecf20Sopenharmony_ci HPI_OSTREAM_HOSTBUFFER_ALLOC : 1728c2ecf20Sopenharmony_ci HPI_ISTREAM_HOSTBUFFER_ALLOC); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci hpi_handle_to_indexes(h_stream, &hm.adapter_index, 1758c2ecf20Sopenharmony_ci &hm.obj_index); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci hm.u.d.u.buffer.buffer_size = size_in_bytes; 1788c2ecf20Sopenharmony_ci hm.u.d.u.buffer.pci_address = pci_address; 1798c2ecf20Sopenharmony_ci hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER; 1808c2ecf20Sopenharmony_ci hpi_send_recv(&hm, &hr); 1818c2ecf20Sopenharmony_ci return hr.error; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic u16 hpi_stream_host_buffer_detach(u32 h_stream) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct hpi_message hm; 1878c2ecf20Sopenharmony_ci struct hpi_response hr; 1888c2ecf20Sopenharmony_ci unsigned int obj = hpi_handle_object(h_stream); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (!h_stream) 1918c2ecf20Sopenharmony_ci return HPI_ERROR_INVALID_OBJ; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci hpi_init_message_response(&hm, &hr, obj, 1948c2ecf20Sopenharmony_ci obj == HPI_OBJ_OSTREAM ? 1958c2ecf20Sopenharmony_ci HPI_OSTREAM_HOSTBUFFER_FREE : 1968c2ecf20Sopenharmony_ci HPI_ISTREAM_HOSTBUFFER_FREE); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci hpi_handle_to_indexes(h_stream, &hm.adapter_index, 1998c2ecf20Sopenharmony_ci &hm.obj_index); 2008c2ecf20Sopenharmony_ci hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER; 2018c2ecf20Sopenharmony_ci hpi_send_recv(&hm, &hr); 2028c2ecf20Sopenharmony_ci return hr.error; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic inline u16 hpi_stream_start(u32 h_stream) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) 2088c2ecf20Sopenharmony_ci return hpi_outstream_start(h_stream); 2098c2ecf20Sopenharmony_ci else 2108c2ecf20Sopenharmony_ci return hpi_instream_start(h_stream); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic inline u16 hpi_stream_stop(u32 h_stream) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) 2168c2ecf20Sopenharmony_ci return hpi_outstream_stop(h_stream); 2178c2ecf20Sopenharmony_ci else 2188c2ecf20Sopenharmony_ci return hpi_instream_stop(h_stream); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic inline u16 hpi_stream_get_info_ex( 2228c2ecf20Sopenharmony_ci u32 h_stream, 2238c2ecf20Sopenharmony_ci u16 *pw_state, 2248c2ecf20Sopenharmony_ci u32 *pbuffer_size, 2258c2ecf20Sopenharmony_ci u32 *pdata_in_buffer, 2268c2ecf20Sopenharmony_ci u32 *psample_count, 2278c2ecf20Sopenharmony_ci u32 *pauxiliary_data 2288c2ecf20Sopenharmony_ci) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci u16 e; 2318c2ecf20Sopenharmony_ci if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) 2328c2ecf20Sopenharmony_ci e = hpi_outstream_get_info_ex(h_stream, pw_state, 2338c2ecf20Sopenharmony_ci pbuffer_size, pdata_in_buffer, 2348c2ecf20Sopenharmony_ci psample_count, pauxiliary_data); 2358c2ecf20Sopenharmony_ci else 2368c2ecf20Sopenharmony_ci e = hpi_instream_get_info_ex(h_stream, pw_state, 2378c2ecf20Sopenharmony_ci pbuffer_size, pdata_in_buffer, 2388c2ecf20Sopenharmony_ci psample_count, pauxiliary_data); 2398c2ecf20Sopenharmony_ci return e; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic inline u16 hpi_stream_group_add( 2438c2ecf20Sopenharmony_ci u32 h_master, 2448c2ecf20Sopenharmony_ci u32 h_stream) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci if (hpi_handle_object(h_master) == HPI_OBJ_OSTREAM) 2478c2ecf20Sopenharmony_ci return hpi_outstream_group_add(h_master, h_stream); 2488c2ecf20Sopenharmony_ci else 2498c2ecf20Sopenharmony_ci return hpi_instream_group_add(h_master, h_stream); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic inline u16 hpi_stream_group_reset(u32 h_stream) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) 2558c2ecf20Sopenharmony_ci return hpi_outstream_group_reset(h_stream); 2568c2ecf20Sopenharmony_ci else 2578c2ecf20Sopenharmony_ci return hpi_instream_group_reset(h_stream); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic u16 handle_error(u16 err, int line, char *filename) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci if (err) 2638c2ecf20Sopenharmony_ci printk(KERN_WARNING 2648c2ecf20Sopenharmony_ci "in file %s, line %d: HPI error %d\n", 2658c2ecf20Sopenharmony_ci filename, line, err); 2668c2ecf20Sopenharmony_ci return err; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci#define hpi_handle_error(x) handle_error(x, __LINE__, __FILE__) 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci/***************************** GENERAL PCM ****************/ 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic void print_hwparams(struct snd_pcm_substream *substream, 2748c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *p) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci char name[16]; 2778c2ecf20Sopenharmony_ci snd_pcm_debug_name(substream, name, sizeof(name)); 2788c2ecf20Sopenharmony_ci snd_printdd("%s HWPARAMS\n", name); 2798c2ecf20Sopenharmony_ci snd_printdd(" samplerate=%dHz channels=%d format=%d subformat=%d\n", 2808c2ecf20Sopenharmony_ci params_rate(p), params_channels(p), 2818c2ecf20Sopenharmony_ci params_format(p), params_subformat(p)); 2828c2ecf20Sopenharmony_ci snd_printdd(" buffer=%dB period=%dB period_size=%dB periods=%d\n", 2838c2ecf20Sopenharmony_ci params_buffer_bytes(p), params_period_bytes(p), 2848c2ecf20Sopenharmony_ci params_period_size(p), params_periods(p)); 2858c2ecf20Sopenharmony_ci snd_printdd(" buffer_size=%d access=%d data_rate=%dB/s\n", 2868c2ecf20Sopenharmony_ci params_buffer_size(p), params_access(p), 2878c2ecf20Sopenharmony_ci params_rate(p) * params_channels(p) * 2888c2ecf20Sopenharmony_ci snd_pcm_format_width(params_format(p)) / 8); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci#define INVALID_FORMAT (__force snd_pcm_format_t)(-1) 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic const snd_pcm_format_t hpi_to_alsa_formats[] = { 2948c2ecf20Sopenharmony_ci INVALID_FORMAT, /* INVALID */ 2958c2ecf20Sopenharmony_ci SNDRV_PCM_FORMAT_U8, /* HPI_FORMAT_PCM8_UNSIGNED 1 */ 2968c2ecf20Sopenharmony_ci SNDRV_PCM_FORMAT_S16, /* HPI_FORMAT_PCM16_SIGNED 2 */ 2978c2ecf20Sopenharmony_ci INVALID_FORMAT, /* HPI_FORMAT_MPEG_L1 3 */ 2988c2ecf20Sopenharmony_ci SNDRV_PCM_FORMAT_MPEG, /* HPI_FORMAT_MPEG_L2 4 */ 2998c2ecf20Sopenharmony_ci SNDRV_PCM_FORMAT_MPEG, /* HPI_FORMAT_MPEG_L3 5 */ 3008c2ecf20Sopenharmony_ci INVALID_FORMAT, /* HPI_FORMAT_DOLBY_AC2 6 */ 3018c2ecf20Sopenharmony_ci INVALID_FORMAT, /* HPI_FORMAT_DOLBY_AC3 7 */ 3028c2ecf20Sopenharmony_ci SNDRV_PCM_FORMAT_S16_BE,/* HPI_FORMAT_PCM16_BIGENDIAN 8 */ 3038c2ecf20Sopenharmony_ci INVALID_FORMAT, /* HPI_FORMAT_AA_TAGIT1_HITS 9 */ 3048c2ecf20Sopenharmony_ci INVALID_FORMAT, /* HPI_FORMAT_AA_TAGIT1_INSERTS 10 */ 3058c2ecf20Sopenharmony_ci SNDRV_PCM_FORMAT_S32, /* HPI_FORMAT_PCM32_SIGNED 11 */ 3068c2ecf20Sopenharmony_ci INVALID_FORMAT, /* HPI_FORMAT_RAW_BITSTREAM 12 */ 3078c2ecf20Sopenharmony_ci INVALID_FORMAT, /* HPI_FORMAT_AA_TAGIT1_HITS_EX1 13 */ 3088c2ecf20Sopenharmony_ci SNDRV_PCM_FORMAT_FLOAT, /* HPI_FORMAT_PCM32_FLOAT 14 */ 3098c2ecf20Sopenharmony_ci#if 1 3108c2ecf20Sopenharmony_ci /* ALSA can't handle 3 byte sample size together with power-of-2 3118c2ecf20Sopenharmony_ci * constraint on buffer_bytes, so disable this format 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_ci INVALID_FORMAT 3148c2ecf20Sopenharmony_ci#else 3158c2ecf20Sopenharmony_ci /* SNDRV_PCM_FORMAT_S24_3LE */ /* HPI_FORMAT_PCM24_SIGNED 15 */ 3168c2ecf20Sopenharmony_ci#endif 3178c2ecf20Sopenharmony_ci}; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int snd_card_asihpi_format_alsa2hpi(snd_pcm_format_t alsa_format, 3218c2ecf20Sopenharmony_ci u16 *hpi_format) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci u16 format; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci for (format = HPI_FORMAT_PCM8_UNSIGNED; 3268c2ecf20Sopenharmony_ci format <= HPI_FORMAT_PCM24_SIGNED; format++) { 3278c2ecf20Sopenharmony_ci if (hpi_to_alsa_formats[format] == alsa_format) { 3288c2ecf20Sopenharmony_ci *hpi_format = format; 3298c2ecf20Sopenharmony_ci return 0; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci snd_printd(KERN_WARNING "failed match for alsa format %d\n", 3348c2ecf20Sopenharmony_ci alsa_format); 3358c2ecf20Sopenharmony_ci *hpi_format = 0; 3368c2ecf20Sopenharmony_ci return -EINVAL; 3378c2ecf20Sopenharmony_ci} 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_cistatic void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi, 3408c2ecf20Sopenharmony_ci struct snd_pcm_hardware *pcmhw) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci u16 err; 3438c2ecf20Sopenharmony_ci u32 h_control; 3448c2ecf20Sopenharmony_ci u32 sample_rate; 3458c2ecf20Sopenharmony_ci int idx; 3468c2ecf20Sopenharmony_ci unsigned int rate_min = 200000; 3478c2ecf20Sopenharmony_ci unsigned int rate_max = 0; 3488c2ecf20Sopenharmony_ci unsigned int rates = 0; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (asihpi->support_mrx) { 3518c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_CONTINUOUS; 3528c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_8000_96000; 3538c2ecf20Sopenharmony_ci rate_min = 8000; 3548c2ecf20Sopenharmony_ci rate_max = 100000; 3558c2ecf20Sopenharmony_ci } else { 3568c2ecf20Sopenharmony_ci /* on cards without SRC, 3578c2ecf20Sopenharmony_ci valid rates are determined by sampleclock */ 3588c2ecf20Sopenharmony_ci err = hpi_mixer_get_control(asihpi->h_mixer, 3598c2ecf20Sopenharmony_ci HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, 3608c2ecf20Sopenharmony_ci HPI_CONTROL_SAMPLECLOCK, &h_control); 3618c2ecf20Sopenharmony_ci if (err) { 3628c2ecf20Sopenharmony_ci dev_err(&asihpi->pci->dev, 3638c2ecf20Sopenharmony_ci "No local sampleclock, err %d\n", err); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci for (idx = -1; idx < 100; idx++) { 3678c2ecf20Sopenharmony_ci if (idx == -1) { 3688c2ecf20Sopenharmony_ci if (hpi_sample_clock_get_sample_rate(h_control, 3698c2ecf20Sopenharmony_ci &sample_rate)) 3708c2ecf20Sopenharmony_ci continue; 3718c2ecf20Sopenharmony_ci } else if (hpi_sample_clock_query_local_rate(h_control, 3728c2ecf20Sopenharmony_ci idx, &sample_rate)) { 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci rate_min = min(rate_min, sample_rate); 3778c2ecf20Sopenharmony_ci rate_max = max(rate_max, sample_rate); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci switch (sample_rate) { 3808c2ecf20Sopenharmony_ci case 5512: 3818c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_5512; 3828c2ecf20Sopenharmony_ci break; 3838c2ecf20Sopenharmony_ci case 8000: 3848c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_8000; 3858c2ecf20Sopenharmony_ci break; 3868c2ecf20Sopenharmony_ci case 11025: 3878c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_11025; 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci case 16000: 3908c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_16000; 3918c2ecf20Sopenharmony_ci break; 3928c2ecf20Sopenharmony_ci case 22050: 3938c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_22050; 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci case 32000: 3968c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_32000; 3978c2ecf20Sopenharmony_ci break; 3988c2ecf20Sopenharmony_ci case 44100: 3998c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_44100; 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci case 48000: 4028c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_48000; 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci case 64000: 4058c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_64000; 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci case 88200: 4088c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_88200; 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci case 96000: 4118c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_96000; 4128c2ecf20Sopenharmony_ci break; 4138c2ecf20Sopenharmony_ci case 176400: 4148c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_176400; 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci case 192000: 4178c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_192000; 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci default: /* some other rate */ 4208c2ecf20Sopenharmony_ci rates |= SNDRV_PCM_RATE_KNOT; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci pcmhw->rates = rates; 4268c2ecf20Sopenharmony_ci pcmhw->rate_min = rate_min; 4278c2ecf20Sopenharmony_ci pcmhw->rate_max = rate_max; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cistatic int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, 4318c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4348c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 4358c2ecf20Sopenharmony_ci struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); 4368c2ecf20Sopenharmony_ci int err; 4378c2ecf20Sopenharmony_ci u16 format; 4388c2ecf20Sopenharmony_ci int width; 4398c2ecf20Sopenharmony_ci unsigned int bytes_per_sec; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci print_hwparams(substream, params); 4428c2ecf20Sopenharmony_ci err = snd_card_asihpi_format_alsa2hpi(params_format(params), &format); 4438c2ecf20Sopenharmony_ci if (err) 4448c2ecf20Sopenharmony_ci return err; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci hpi_handle_error(hpi_format_create(&dpcm->format, 4478c2ecf20Sopenharmony_ci params_channels(params), 4488c2ecf20Sopenharmony_ci format, params_rate(params), 0, 0)); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 4518c2ecf20Sopenharmony_ci if (hpi_instream_reset(dpcm->h_stream) != 0) 4528c2ecf20Sopenharmony_ci return -EINVAL; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (hpi_instream_set_format( 4558c2ecf20Sopenharmony_ci dpcm->h_stream, &dpcm->format) != 0) 4568c2ecf20Sopenharmony_ci return -EINVAL; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci dpcm->hpi_buffer_attached = 0; 4608c2ecf20Sopenharmony_ci if (card->can_dma) { 4618c2ecf20Sopenharmony_ci err = hpi_stream_host_buffer_attach(dpcm->h_stream, 4628c2ecf20Sopenharmony_ci params_buffer_bytes(params), runtime->dma_addr); 4638c2ecf20Sopenharmony_ci if (err == 0) { 4648c2ecf20Sopenharmony_ci snd_printdd( 4658c2ecf20Sopenharmony_ci "stream_host_buffer_attach success %u %lu\n", 4668c2ecf20Sopenharmony_ci params_buffer_bytes(params), 4678c2ecf20Sopenharmony_ci (unsigned long)runtime->dma_addr); 4688c2ecf20Sopenharmony_ci } else { 4698c2ecf20Sopenharmony_ci snd_printd("stream_host_buffer_attach error %d\n", 4708c2ecf20Sopenharmony_ci err); 4718c2ecf20Sopenharmony_ci return -ENOMEM; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci err = hpi_stream_get_info_ex(dpcm->h_stream, NULL, 4758c2ecf20Sopenharmony_ci &dpcm->hpi_buffer_attached, NULL, NULL, NULL); 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci bytes_per_sec = params_rate(params) * params_channels(params); 4788c2ecf20Sopenharmony_ci width = snd_pcm_format_width(params_format(params)); 4798c2ecf20Sopenharmony_ci bytes_per_sec *= width; 4808c2ecf20Sopenharmony_ci bytes_per_sec /= 8; 4818c2ecf20Sopenharmony_ci if (width < 0 || bytes_per_sec == 0) 4828c2ecf20Sopenharmony_ci return -EINVAL; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci dpcm->bytes_per_sec = bytes_per_sec; 4858c2ecf20Sopenharmony_ci dpcm->buffer_bytes = params_buffer_bytes(params); 4868c2ecf20Sopenharmony_ci dpcm->period_bytes = params_period_bytes(params); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_cistatic int 4928c2ecf20Sopenharmony_cisnd_card_asihpi_hw_free(struct snd_pcm_substream *substream) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 4958c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 4968c2ecf20Sopenharmony_ci if (dpcm->hpi_buffer_attached) 4978c2ecf20Sopenharmony_ci hpi_stream_host_buffer_detach(dpcm->h_stream); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return 0; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic void snd_card_asihpi_runtime_free(struct snd_pcm_runtime *runtime) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 5058c2ecf20Sopenharmony_ci kfree(dpcm); 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream * 5098c2ecf20Sopenharmony_ci substream) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 5128c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 5138c2ecf20Sopenharmony_ci int expiry; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci expiry = HZ / 200; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci expiry = max(expiry, 1); /* don't let it be zero! */ 5188c2ecf20Sopenharmony_ci mod_timer(&dpcm->timer, jiffies + expiry); 5198c2ecf20Sopenharmony_ci dpcm->respawn_timer = 1; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_cistatic void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 5258c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci dpcm->respawn_timer = 0; 5288c2ecf20Sopenharmony_ci del_timer(&dpcm->timer); 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm; 5348c2ecf20Sopenharmony_ci struct snd_card_asihpi *card; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data; 5378c2ecf20Sopenharmony_ci card = snd_pcm_substream_chip(substream); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci WARN_ON(in_interrupt()); 5408c2ecf20Sopenharmony_ci card->llmode_streampriv = dpcm; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index, 5438c2ecf20Sopenharmony_ci HPI_ADAPTER_PROPERTY_IRQ_RATE, 5448c2ecf20Sopenharmony_ci card->update_interval_frames, 0)); 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct snd_card_asihpi *card; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci card = snd_pcm_substream_chip(substream); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index, 5548c2ecf20Sopenharmony_ci HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0)); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci card->llmode_streampriv = NULL; 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, 5608c2ecf20Sopenharmony_ci int cmd) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data; 5638c2ecf20Sopenharmony_ci struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); 5648c2ecf20Sopenharmony_ci struct snd_pcm_substream *s; 5658c2ecf20Sopenharmony_ci u16 e; 5668c2ecf20Sopenharmony_ci char name[16]; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci snd_pcm_debug_name(substream, name, sizeof(name)); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci switch (cmd) { 5718c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 5728c2ecf20Sopenharmony_ci snd_printdd("%s trigger start\n", name); 5738c2ecf20Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 5748c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = s->runtime; 5758c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *ds = runtime->private_data; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (snd_pcm_substream_chip(s) != card) 5788c2ecf20Sopenharmony_ci continue; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* don't link Cap and Play */ 5818c2ecf20Sopenharmony_ci if (substream->stream != s->stream) 5828c2ecf20Sopenharmony_ci continue; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci ds->drained_count = 0; 5858c2ecf20Sopenharmony_ci if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { 5868c2ecf20Sopenharmony_ci /* How do I know how much valid data is present 5878c2ecf20Sopenharmony_ci * in buffer? Must be at least one period! 5888c2ecf20Sopenharmony_ci * Guessing 2 periods, but if 5898c2ecf20Sopenharmony_ci * buffer is bigger it may contain even more 5908c2ecf20Sopenharmony_ci * data?? 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_ci unsigned int preload = ds->period_bytes * 1; 5938c2ecf20Sopenharmony_ci snd_printddd("%d preload %d\n", s->number, preload); 5948c2ecf20Sopenharmony_ci hpi_handle_error(hpi_outstream_write_buf( 5958c2ecf20Sopenharmony_ci ds->h_stream, 5968c2ecf20Sopenharmony_ci &runtime->dma_area[0], 5978c2ecf20Sopenharmony_ci preload, 5988c2ecf20Sopenharmony_ci &ds->format)); 5998c2ecf20Sopenharmony_ci ds->pcm_buf_host_rw_ofs = preload; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (card->support_grouping) { 6038c2ecf20Sopenharmony_ci snd_printdd("%d group\n", s->number); 6048c2ecf20Sopenharmony_ci e = hpi_stream_group_add( 6058c2ecf20Sopenharmony_ci dpcm->h_stream, 6068c2ecf20Sopenharmony_ci ds->h_stream); 6078c2ecf20Sopenharmony_ci if (!e) { 6088c2ecf20Sopenharmony_ci snd_pcm_trigger_done(s, substream); 6098c2ecf20Sopenharmony_ci } else { 6108c2ecf20Sopenharmony_ci hpi_handle_error(e); 6118c2ecf20Sopenharmony_ci break; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci } else 6148c2ecf20Sopenharmony_ci break; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci /* start the master stream */ 6178c2ecf20Sopenharmony_ci card->pcm_start(substream); 6188c2ecf20Sopenharmony_ci if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) || 6198c2ecf20Sopenharmony_ci !card->can_dma) 6208c2ecf20Sopenharmony_ci hpi_handle_error(hpi_stream_start(dpcm->h_stream)); 6218c2ecf20Sopenharmony_ci break; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 6248c2ecf20Sopenharmony_ci snd_printdd("%s trigger stop\n", name); 6258c2ecf20Sopenharmony_ci card->pcm_stop(substream); 6268c2ecf20Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 6278c2ecf20Sopenharmony_ci if (snd_pcm_substream_chip(s) != card) 6288c2ecf20Sopenharmony_ci continue; 6298c2ecf20Sopenharmony_ci /* don't link Cap and Play */ 6308c2ecf20Sopenharmony_ci if (substream->stream != s->stream) 6318c2ecf20Sopenharmony_ci continue; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci /*? workaround linked streams don't 6348c2ecf20Sopenharmony_ci transition to SETUP 20070706*/ 6358c2ecf20Sopenharmony_ci s->runtime->status->state = SNDRV_PCM_STATE_SETUP; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci if (card->support_grouping) { 6388c2ecf20Sopenharmony_ci snd_printdd("%d group\n", s->number); 6398c2ecf20Sopenharmony_ci snd_pcm_trigger_done(s, substream); 6408c2ecf20Sopenharmony_ci } else 6418c2ecf20Sopenharmony_ci break; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci /* _prepare and _hwparams reset the stream */ 6458c2ecf20Sopenharmony_ci hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); 6468c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 6478c2ecf20Sopenharmony_ci hpi_handle_error( 6488c2ecf20Sopenharmony_ci hpi_outstream_reset(dpcm->h_stream)); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (card->support_grouping) 6518c2ecf20Sopenharmony_ci hpi_handle_error(hpi_stream_group_reset(dpcm->h_stream)); 6528c2ecf20Sopenharmony_ci break; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 6558c2ecf20Sopenharmony_ci snd_printdd("%s trigger pause release\n", name); 6568c2ecf20Sopenharmony_ci card->pcm_start(substream); 6578c2ecf20Sopenharmony_ci hpi_handle_error(hpi_stream_start(dpcm->h_stream)); 6588c2ecf20Sopenharmony_ci break; 6598c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 6608c2ecf20Sopenharmony_ci snd_printdd("%s trigger pause push\n", name); 6618c2ecf20Sopenharmony_ci card->pcm_stop(substream); 6628c2ecf20Sopenharmony_ci hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); 6638c2ecf20Sopenharmony_ci break; 6648c2ecf20Sopenharmony_ci default: 6658c2ecf20Sopenharmony_ci snd_printd(KERN_ERR "\tINVALID\n"); 6668c2ecf20Sopenharmony_ci return -EINVAL; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci return 0; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci/*algorithm outline 6738c2ecf20Sopenharmony_ci Without linking degenerates to getting single stream pos etc 6748c2ecf20Sopenharmony_ci Without mmap 2nd loop degenerates to snd_pcm_period_elapsed 6758c2ecf20Sopenharmony_ci*/ 6768c2ecf20Sopenharmony_ci/* 6778c2ecf20Sopenharmony_cipcm_buf_dma_ofs=get_buf_pos(s); 6788c2ecf20Sopenharmony_cifor_each_linked_stream(s) { 6798c2ecf20Sopenharmony_ci pcm_buf_dma_ofs=get_buf_pos(s); 6808c2ecf20Sopenharmony_ci min_buf_pos = modulo_min(min_buf_pos, pcm_buf_dma_ofs, buffer_bytes) 6818c2ecf20Sopenharmony_ci new_data = min(new_data, calc_new_data(pcm_buf_dma_ofs,irq_pos) 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_citimer.expires = jiffies + predict_next_period_ready(min_buf_pos); 6848c2ecf20Sopenharmony_cifor_each_linked_stream(s) { 6858c2ecf20Sopenharmony_ci s->pcm_buf_dma_ofs = min_buf_pos; 6868c2ecf20Sopenharmony_ci if (new_data > period_bytes) { 6878c2ecf20Sopenharmony_ci if (mmap) { 6888c2ecf20Sopenharmony_ci irq_pos = (irq_pos + period_bytes) % buffer_bytes; 6898c2ecf20Sopenharmony_ci if (playback) { 6908c2ecf20Sopenharmony_ci write(period_bytes); 6918c2ecf20Sopenharmony_ci } else { 6928c2ecf20Sopenharmony_ci read(period_bytes); 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci } 6958c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(s); 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci*/ 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci/** Minimum of 2 modulo values. Works correctly when the difference between 7018c2ecf20Sopenharmony_ci* the values is less than half the modulus 7028c2ecf20Sopenharmony_ci*/ 7038c2ecf20Sopenharmony_cistatic inline unsigned int modulo_min(unsigned int a, unsigned int b, 7048c2ecf20Sopenharmony_ci unsigned long int modulus) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci unsigned int result; 7078c2ecf20Sopenharmony_ci if (((a-b) % modulus) < (modulus/2)) 7088c2ecf20Sopenharmony_ci result = b; 7098c2ecf20Sopenharmony_ci else 7108c2ecf20Sopenharmony_ci result = a; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci return result; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci/** Timer function, equivalent to interrupt service routine for cards 7168c2ecf20Sopenharmony_ci*/ 7178c2ecf20Sopenharmony_cistatic void snd_card_asihpi_timer_function(struct timer_list *t) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = from_timer(dpcm, t, timer); 7208c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream = dpcm->substream; 7218c2ecf20Sopenharmony_ci struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); 7228c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 7238c2ecf20Sopenharmony_ci struct snd_pcm_substream *s; 7248c2ecf20Sopenharmony_ci unsigned int newdata = 0; 7258c2ecf20Sopenharmony_ci unsigned int pcm_buf_dma_ofs, min_buf_pos = 0; 7268c2ecf20Sopenharmony_ci unsigned int remdata, xfercount, next_jiffies; 7278c2ecf20Sopenharmony_ci int first = 1; 7288c2ecf20Sopenharmony_ci int loops = 0; 7298c2ecf20Sopenharmony_ci u16 state; 7308c2ecf20Sopenharmony_ci u32 buffer_size, bytes_avail, samples_played, on_card_bytes; 7318c2ecf20Sopenharmony_ci char name[16]; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci snd_pcm_debug_name(substream, name, sizeof(name)); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci /* find minimum newdata and buffer pos in group */ 7378c2ecf20Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 7388c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *ds = s->runtime->private_data; 7398c2ecf20Sopenharmony_ci runtime = s->runtime; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if (snd_pcm_substream_chip(s) != card) 7428c2ecf20Sopenharmony_ci continue; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* don't link Cap and Play */ 7458c2ecf20Sopenharmony_ci if (substream->stream != s->stream) 7468c2ecf20Sopenharmony_ci continue; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci hpi_handle_error(hpi_stream_get_info_ex( 7498c2ecf20Sopenharmony_ci ds->h_stream, &state, 7508c2ecf20Sopenharmony_ci &buffer_size, &bytes_avail, 7518c2ecf20Sopenharmony_ci &samples_played, &on_card_bytes)); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* number of bytes in on-card buffer */ 7548c2ecf20Sopenharmony_ci runtime->delay = on_card_bytes; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci if (!card->can_dma) 7578c2ecf20Sopenharmony_ci on_card_bytes = bytes_avail; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { 7608c2ecf20Sopenharmony_ci pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail; 7618c2ecf20Sopenharmony_ci if (state == HPI_STATE_STOPPED) { 7628c2ecf20Sopenharmony_ci if (bytes_avail == 0) { 7638c2ecf20Sopenharmony_ci hpi_handle_error(hpi_stream_start(ds->h_stream)); 7648c2ecf20Sopenharmony_ci snd_printdd("P%d start\n", s->number); 7658c2ecf20Sopenharmony_ci ds->drained_count = 0; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci } else if (state == HPI_STATE_DRAINED) { 7688c2ecf20Sopenharmony_ci snd_printd(KERN_WARNING "P%d drained\n", 7698c2ecf20Sopenharmony_ci s->number); 7708c2ecf20Sopenharmony_ci ds->drained_count++; 7718c2ecf20Sopenharmony_ci if (ds->drained_count > 20) { 7728c2ecf20Sopenharmony_ci snd_pcm_stop_xrun(s); 7738c2ecf20Sopenharmony_ci continue; 7748c2ecf20Sopenharmony_ci } 7758c2ecf20Sopenharmony_ci } else { 7768c2ecf20Sopenharmony_ci ds->drained_count = 0; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci } else 7798c2ecf20Sopenharmony_ci pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (first) { 7828c2ecf20Sopenharmony_ci /* can't statically init min when wrap is involved */ 7838c2ecf20Sopenharmony_ci min_buf_pos = pcm_buf_dma_ofs; 7848c2ecf20Sopenharmony_ci newdata = (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes; 7858c2ecf20Sopenharmony_ci first = 0; 7868c2ecf20Sopenharmony_ci } else { 7878c2ecf20Sopenharmony_ci min_buf_pos = 7888c2ecf20Sopenharmony_ci modulo_min(min_buf_pos, pcm_buf_dma_ofs, UINT_MAX+1L); 7898c2ecf20Sopenharmony_ci newdata = min( 7908c2ecf20Sopenharmony_ci (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes, 7918c2ecf20Sopenharmony_ci newdata); 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci snd_printddd( 7958c2ecf20Sopenharmony_ci "timer1, %s, %d, S=%d, elap=%d, rw=%d, dsp=%d, left=%d, aux=%d, space=%d, hw_ptr=%ld, appl_ptr=%ld\n", 7968c2ecf20Sopenharmony_ci name, s->number, state, 7978c2ecf20Sopenharmony_ci ds->pcm_buf_elapsed_dma_ofs, 7988c2ecf20Sopenharmony_ci ds->pcm_buf_host_rw_ofs, 7998c2ecf20Sopenharmony_ci pcm_buf_dma_ofs, 8008c2ecf20Sopenharmony_ci (int)bytes_avail, 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci (int)on_card_bytes, 8038c2ecf20Sopenharmony_ci buffer_size-bytes_avail, 8048c2ecf20Sopenharmony_ci (unsigned long)frames_to_bytes(runtime, 8058c2ecf20Sopenharmony_ci runtime->status->hw_ptr), 8068c2ecf20Sopenharmony_ci (unsigned long)frames_to_bytes(runtime, 8078c2ecf20Sopenharmony_ci runtime->control->appl_ptr) 8088c2ecf20Sopenharmony_ci ); 8098c2ecf20Sopenharmony_ci loops++; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci pcm_buf_dma_ofs = min_buf_pos; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci remdata = newdata % dpcm->period_bytes; 8148c2ecf20Sopenharmony_ci xfercount = newdata - remdata; /* a multiple of period_bytes */ 8158c2ecf20Sopenharmony_ci /* come back when on_card_bytes has decreased enough to allow 8168c2ecf20Sopenharmony_ci write to happen, or when data has been consumed to make another 8178c2ecf20Sopenharmony_ci period 8188c2ecf20Sopenharmony_ci */ 8198c2ecf20Sopenharmony_ci if (xfercount && (on_card_bytes > dpcm->period_bytes)) 8208c2ecf20Sopenharmony_ci next_jiffies = ((on_card_bytes - dpcm->period_bytes) * HZ / dpcm->bytes_per_sec); 8218c2ecf20Sopenharmony_ci else 8228c2ecf20Sopenharmony_ci next_jiffies = ((dpcm->period_bytes - remdata) * HZ / dpcm->bytes_per_sec); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci next_jiffies = max(next_jiffies, 1U); 8258c2ecf20Sopenharmony_ci dpcm->timer.expires = jiffies + next_jiffies; 8268c2ecf20Sopenharmony_ci snd_printddd("timer2, jif=%d, buf_pos=%d, newdata=%d, xfer=%d\n", 8278c2ecf20Sopenharmony_ci next_jiffies, pcm_buf_dma_ofs, newdata, xfercount); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 8308c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *ds = s->runtime->private_data; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci /* don't link Cap and Play */ 8338c2ecf20Sopenharmony_ci if (substream->stream != s->stream) 8348c2ecf20Sopenharmony_ci continue; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci /* Store dma offset for use by pointer callback */ 8378c2ecf20Sopenharmony_ci ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci if (xfercount && 8408c2ecf20Sopenharmony_ci /* Limit use of on card fifo for playback */ 8418c2ecf20Sopenharmony_ci ((on_card_bytes <= ds->period_bytes) || 8428c2ecf20Sopenharmony_ci (s->stream == SNDRV_PCM_STREAM_CAPTURE))) 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci { 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci unsigned int buf_ofs = ds->pcm_buf_host_rw_ofs % ds->buffer_bytes; 8478c2ecf20Sopenharmony_ci unsigned int xfer1, xfer2; 8488c2ecf20Sopenharmony_ci char *pd = &s->runtime->dma_area[buf_ofs]; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci if (card->can_dma) { /* buffer wrap is handled at lower level */ 8518c2ecf20Sopenharmony_ci xfer1 = xfercount; 8528c2ecf20Sopenharmony_ci xfer2 = 0; 8538c2ecf20Sopenharmony_ci } else { 8548c2ecf20Sopenharmony_ci xfer1 = min(xfercount, ds->buffer_bytes - buf_ofs); 8558c2ecf20Sopenharmony_ci xfer2 = xfercount - xfer1; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { 8598c2ecf20Sopenharmony_ci snd_printddd("write1, P=%d, xfer=%d, buf_ofs=%d\n", 8608c2ecf20Sopenharmony_ci s->number, xfer1, buf_ofs); 8618c2ecf20Sopenharmony_ci hpi_handle_error( 8628c2ecf20Sopenharmony_ci hpi_outstream_write_buf( 8638c2ecf20Sopenharmony_ci ds->h_stream, pd, xfer1, 8648c2ecf20Sopenharmony_ci &ds->format)); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci if (xfer2) { 8678c2ecf20Sopenharmony_ci pd = s->runtime->dma_area; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci snd_printddd("write2, P=%d, xfer=%d, buf_ofs=%d\n", 8708c2ecf20Sopenharmony_ci s->number, 8718c2ecf20Sopenharmony_ci xfercount - xfer1, buf_ofs); 8728c2ecf20Sopenharmony_ci hpi_handle_error( 8738c2ecf20Sopenharmony_ci hpi_outstream_write_buf( 8748c2ecf20Sopenharmony_ci ds->h_stream, pd, 8758c2ecf20Sopenharmony_ci xfercount - xfer1, 8768c2ecf20Sopenharmony_ci &ds->format)); 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci } else { 8798c2ecf20Sopenharmony_ci snd_printddd("read1, C=%d, xfer=%d\n", 8808c2ecf20Sopenharmony_ci s->number, xfer1); 8818c2ecf20Sopenharmony_ci hpi_handle_error( 8828c2ecf20Sopenharmony_ci hpi_instream_read_buf( 8838c2ecf20Sopenharmony_ci ds->h_stream, 8848c2ecf20Sopenharmony_ci pd, xfer1)); 8858c2ecf20Sopenharmony_ci if (xfer2) { 8868c2ecf20Sopenharmony_ci pd = s->runtime->dma_area; 8878c2ecf20Sopenharmony_ci snd_printddd("read2, C=%d, xfer=%d\n", 8888c2ecf20Sopenharmony_ci s->number, xfer2); 8898c2ecf20Sopenharmony_ci hpi_handle_error( 8908c2ecf20Sopenharmony_ci hpi_instream_read_buf( 8918c2ecf20Sopenharmony_ci ds->h_stream, 8928c2ecf20Sopenharmony_ci pd, xfer2)); 8938c2ecf20Sopenharmony_ci } 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci /* ? host_rw_ofs always ahead of elapsed_dma_ofs by preload size? */ 8968c2ecf20Sopenharmony_ci ds->pcm_buf_host_rw_ofs += xfercount; 8978c2ecf20Sopenharmony_ci ds->pcm_buf_elapsed_dma_ofs += xfercount; 8988c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(s); 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci if (!card->hpi->interrupt_mode && dpcm->respawn_timer) 9038c2ecf20Sopenharmony_ci add_timer(&dpcm->timer); 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cistatic void snd_card_asihpi_isr(struct hpi_adapter *a) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci struct snd_card_asihpi *asihpi; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci WARN_ON(!a || !a->snd_card || !a->snd_card->private_data); 9118c2ecf20Sopenharmony_ci asihpi = (struct snd_card_asihpi *)a->snd_card->private_data; 9128c2ecf20Sopenharmony_ci if (asihpi->llmode_streampriv) 9138c2ecf20Sopenharmony_ci snd_card_asihpi_timer_function( 9148c2ecf20Sopenharmony_ci &asihpi->llmode_streampriv->timer); 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci/***************************** PLAYBACK OPS ****************/ 9188c2ecf20Sopenharmony_cistatic int snd_card_asihpi_playback_prepare(struct snd_pcm_substream * 9198c2ecf20Sopenharmony_ci substream) 9208c2ecf20Sopenharmony_ci{ 9218c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 9228c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci snd_printdd("P%d prepare\n", substream->number); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci hpi_handle_error(hpi_outstream_reset(dpcm->h_stream)); 9278c2ecf20Sopenharmony_ci dpcm->pcm_buf_host_rw_ofs = 0; 9288c2ecf20Sopenharmony_ci dpcm->pcm_buf_dma_ofs = 0; 9298c2ecf20Sopenharmony_ci dpcm->pcm_buf_elapsed_dma_ofs = 0; 9308c2ecf20Sopenharmony_ci return 0; 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t 9348c2ecf20Sopenharmony_cisnd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 9378c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 9388c2ecf20Sopenharmony_ci snd_pcm_uframes_t ptr; 9398c2ecf20Sopenharmony_ci char name[16]; 9408c2ecf20Sopenharmony_ci snd_pcm_debug_name(substream, name, sizeof(name)); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes); 9438c2ecf20Sopenharmony_ci snd_printddd("%s, pointer=%ld\n", name, (unsigned long)ptr); 9448c2ecf20Sopenharmony_ci return ptr; 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_cistatic u64 snd_card_asihpi_playback_formats(struct snd_card_asihpi *asihpi, 9488c2ecf20Sopenharmony_ci u32 h_stream) 9498c2ecf20Sopenharmony_ci{ 9508c2ecf20Sopenharmony_ci struct hpi_format hpi_format; 9518c2ecf20Sopenharmony_ci u16 format; 9528c2ecf20Sopenharmony_ci u16 err; 9538c2ecf20Sopenharmony_ci u32 h_control; 9548c2ecf20Sopenharmony_ci u32 sample_rate = 48000; 9558c2ecf20Sopenharmony_ci u64 formats = 0; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci /* on cards without SRC, must query at valid rate, 9588c2ecf20Sopenharmony_ci * maybe set by external sync 9598c2ecf20Sopenharmony_ci */ 9608c2ecf20Sopenharmony_ci err = hpi_mixer_get_control(asihpi->h_mixer, 9618c2ecf20Sopenharmony_ci HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, 9628c2ecf20Sopenharmony_ci HPI_CONTROL_SAMPLECLOCK, &h_control); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (!err) 9658c2ecf20Sopenharmony_ci err = hpi_sample_clock_get_sample_rate(h_control, 9668c2ecf20Sopenharmony_ci &sample_rate); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci for (format = HPI_FORMAT_PCM8_UNSIGNED; 9698c2ecf20Sopenharmony_ci format <= HPI_FORMAT_PCM24_SIGNED; format++) { 9708c2ecf20Sopenharmony_ci err = hpi_format_create(&hpi_format, asihpi->out_max_chans, 9718c2ecf20Sopenharmony_ci format, sample_rate, 128000, 0); 9728c2ecf20Sopenharmony_ci if (!err) 9738c2ecf20Sopenharmony_ci err = hpi_outstream_query_format(h_stream, &hpi_format); 9748c2ecf20Sopenharmony_ci if (!err && (hpi_to_alsa_formats[format] != INVALID_FORMAT)) 9758c2ecf20Sopenharmony_ci formats |= pcm_format_to_bits(hpi_to_alsa_formats[format]); 9768c2ecf20Sopenharmony_ci } 9778c2ecf20Sopenharmony_ci return formats; 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 9838c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm; 9848c2ecf20Sopenharmony_ci struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); 9858c2ecf20Sopenharmony_ci struct snd_pcm_hardware snd_card_asihpi_playback; 9868c2ecf20Sopenharmony_ci int err; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); 9898c2ecf20Sopenharmony_ci if (dpcm == NULL) 9908c2ecf20Sopenharmony_ci return -ENOMEM; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci err = hpi_outstream_open(card->hpi->adapter->index, 9938c2ecf20Sopenharmony_ci substream->number, &dpcm->h_stream); 9948c2ecf20Sopenharmony_ci hpi_handle_error(err); 9958c2ecf20Sopenharmony_ci if (err) 9968c2ecf20Sopenharmony_ci kfree(dpcm); 9978c2ecf20Sopenharmony_ci if (err == HPI_ERROR_OBJ_ALREADY_OPEN) 9988c2ecf20Sopenharmony_ci return -EBUSY; 9998c2ecf20Sopenharmony_ci if (err) 10008c2ecf20Sopenharmony_ci return -EIO; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci /*? also check ASI5000 samplerate source 10038c2ecf20Sopenharmony_ci If external, only support external rate. 10048c2ecf20Sopenharmony_ci If internal and other stream playing, can't switch 10058c2ecf20Sopenharmony_ci */ 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0); 10088c2ecf20Sopenharmony_ci dpcm->substream = substream; 10098c2ecf20Sopenharmony_ci runtime->private_data = dpcm; 10108c2ecf20Sopenharmony_ci runtime->private_free = snd_card_asihpi_runtime_free; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci memset(&snd_card_asihpi_playback, 0, sizeof(snd_card_asihpi_playback)); 10138c2ecf20Sopenharmony_ci if (!card->hpi->interrupt_mode) { 10148c2ecf20Sopenharmony_ci snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX; 10158c2ecf20Sopenharmony_ci snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN; 10168c2ecf20Sopenharmony_ci snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; 10178c2ecf20Sopenharmony_ci snd_card_asihpi_playback.periods_min = PERIODS_MIN; 10188c2ecf20Sopenharmony_ci snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN; 10198c2ecf20Sopenharmony_ci } else { 10208c2ecf20Sopenharmony_ci size_t pbmin = card->update_interval_frames * 10218c2ecf20Sopenharmony_ci card->out_max_chans; 10228c2ecf20Sopenharmony_ci snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX; 10238c2ecf20Sopenharmony_ci snd_card_asihpi_playback.period_bytes_min = pbmin; 10248c2ecf20Sopenharmony_ci snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; 10258c2ecf20Sopenharmony_ci snd_card_asihpi_playback.periods_min = PERIODS_MIN; 10268c2ecf20Sopenharmony_ci snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / pbmin; 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci /* snd_card_asihpi_playback.fifo_size = 0; */ 10308c2ecf20Sopenharmony_ci snd_card_asihpi_playback.channels_max = card->out_max_chans; 10318c2ecf20Sopenharmony_ci snd_card_asihpi_playback.channels_min = card->out_min_chans; 10328c2ecf20Sopenharmony_ci snd_card_asihpi_playback.formats = 10338c2ecf20Sopenharmony_ci snd_card_asihpi_playback_formats(card, dpcm->h_stream); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_playback); 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci snd_card_asihpi_playback.info = SNDRV_PCM_INFO_INTERLEAVED | 10388c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_DOUBLE | 10398c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BATCH | 10408c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 10418c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | 10428c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 10438c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci if (card->support_grouping) { 10468c2ecf20Sopenharmony_ci snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START; 10478c2ecf20Sopenharmony_ci snd_pcm_set_sync(substream); 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci /* struct is copied, so can create initializer dynamically */ 10518c2ecf20Sopenharmony_ci runtime->hw = snd_card_asihpi_playback; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci if (card->can_dma) 10548c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_pow2(runtime, 0, 10558c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_BYTES); 10568c2ecf20Sopenharmony_ci if (err < 0) 10578c2ecf20Sopenharmony_ci return err; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 10608c2ecf20Sopenharmony_ci card->update_interval_frames); 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 10638c2ecf20Sopenharmony_ci card->update_interval_frames, UINT_MAX); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci snd_printdd("playback open\n"); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci return 0; 10688c2ecf20Sopenharmony_ci} 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_cistatic int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream) 10718c2ecf20Sopenharmony_ci{ 10728c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 10738c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci hpi_handle_error(hpi_outstream_close(dpcm->h_stream)); 10768c2ecf20Sopenharmony_ci snd_printdd("playback close\n"); 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci return 0; 10798c2ecf20Sopenharmony_ci} 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = { 10828c2ecf20Sopenharmony_ci .open = snd_card_asihpi_playback_open, 10838c2ecf20Sopenharmony_ci .close = snd_card_asihpi_playback_close, 10848c2ecf20Sopenharmony_ci .hw_params = snd_card_asihpi_pcm_hw_params, 10858c2ecf20Sopenharmony_ci .hw_free = snd_card_asihpi_hw_free, 10868c2ecf20Sopenharmony_ci .prepare = snd_card_asihpi_playback_prepare, 10878c2ecf20Sopenharmony_ci .trigger = snd_card_asihpi_trigger, 10888c2ecf20Sopenharmony_ci .pointer = snd_card_asihpi_playback_pointer, 10898c2ecf20Sopenharmony_ci}; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci/***************************** CAPTURE OPS ****************/ 10928c2ecf20Sopenharmony_cistatic snd_pcm_uframes_t 10938c2ecf20Sopenharmony_cisnd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream) 10948c2ecf20Sopenharmony_ci{ 10958c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 10968c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 10978c2ecf20Sopenharmony_ci char name[16]; 10988c2ecf20Sopenharmony_ci snd_pcm_debug_name(substream, name, sizeof(name)); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci snd_printddd("%s, pointer=%d\n", name, dpcm->pcm_buf_dma_ofs); 11018c2ecf20Sopenharmony_ci /* NOTE Unlike playback can't use actual samples_played 11028c2ecf20Sopenharmony_ci for the capture position, because those samples aren't yet in 11038c2ecf20Sopenharmony_ci the local buffer available for reading. 11048c2ecf20Sopenharmony_ci */ 11058c2ecf20Sopenharmony_ci return bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes); 11068c2ecf20Sopenharmony_ci} 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cistatic int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream) 11098c2ecf20Sopenharmony_ci{ 11108c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 11118c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci hpi_handle_error(hpi_instream_reset(dpcm->h_stream)); 11148c2ecf20Sopenharmony_ci dpcm->pcm_buf_host_rw_ofs = 0; 11158c2ecf20Sopenharmony_ci dpcm->pcm_buf_dma_ofs = 0; 11168c2ecf20Sopenharmony_ci dpcm->pcm_buf_elapsed_dma_ofs = 0; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci snd_printdd("Capture Prepare %d\n", substream->number); 11198c2ecf20Sopenharmony_ci return 0; 11208c2ecf20Sopenharmony_ci} 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_cistatic u64 snd_card_asihpi_capture_formats(struct snd_card_asihpi *asihpi, 11238c2ecf20Sopenharmony_ci u32 h_stream) 11248c2ecf20Sopenharmony_ci{ 11258c2ecf20Sopenharmony_ci struct hpi_format hpi_format; 11268c2ecf20Sopenharmony_ci u16 format; 11278c2ecf20Sopenharmony_ci u16 err; 11288c2ecf20Sopenharmony_ci u32 h_control; 11298c2ecf20Sopenharmony_ci u32 sample_rate = 48000; 11308c2ecf20Sopenharmony_ci u64 formats = 0; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci /* on cards without SRC, must query at valid rate, 11338c2ecf20Sopenharmony_ci maybe set by external sync */ 11348c2ecf20Sopenharmony_ci err = hpi_mixer_get_control(asihpi->h_mixer, 11358c2ecf20Sopenharmony_ci HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, 11368c2ecf20Sopenharmony_ci HPI_CONTROL_SAMPLECLOCK, &h_control); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci if (!err) 11398c2ecf20Sopenharmony_ci err = hpi_sample_clock_get_sample_rate(h_control, 11408c2ecf20Sopenharmony_ci &sample_rate); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci for (format = HPI_FORMAT_PCM8_UNSIGNED; 11438c2ecf20Sopenharmony_ci format <= HPI_FORMAT_PCM24_SIGNED; format++) { 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci err = hpi_format_create(&hpi_format, asihpi->in_max_chans, 11468c2ecf20Sopenharmony_ci format, sample_rate, 128000, 0); 11478c2ecf20Sopenharmony_ci if (!err) 11488c2ecf20Sopenharmony_ci err = hpi_instream_query_format(h_stream, &hpi_format); 11498c2ecf20Sopenharmony_ci if (!err && (hpi_to_alsa_formats[format] != INVALID_FORMAT)) 11508c2ecf20Sopenharmony_ci formats |= pcm_format_to_bits(hpi_to_alsa_formats[format]); 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci return formats; 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_cistatic int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 11588c2ecf20Sopenharmony_ci struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); 11598c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm; 11608c2ecf20Sopenharmony_ci struct snd_pcm_hardware snd_card_asihpi_capture; 11618c2ecf20Sopenharmony_ci int err; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); 11648c2ecf20Sopenharmony_ci if (dpcm == NULL) 11658c2ecf20Sopenharmony_ci return -ENOMEM; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci snd_printdd("capture open adapter %d stream %d\n", 11688c2ecf20Sopenharmony_ci card->hpi->adapter->index, substream->number); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci err = hpi_handle_error( 11718c2ecf20Sopenharmony_ci hpi_instream_open(card->hpi->adapter->index, 11728c2ecf20Sopenharmony_ci substream->number, &dpcm->h_stream)); 11738c2ecf20Sopenharmony_ci if (err) 11748c2ecf20Sopenharmony_ci kfree(dpcm); 11758c2ecf20Sopenharmony_ci if (err == HPI_ERROR_OBJ_ALREADY_OPEN) 11768c2ecf20Sopenharmony_ci return -EBUSY; 11778c2ecf20Sopenharmony_ci if (err) 11788c2ecf20Sopenharmony_ci return -EIO; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0); 11818c2ecf20Sopenharmony_ci dpcm->substream = substream; 11828c2ecf20Sopenharmony_ci runtime->private_data = dpcm; 11838c2ecf20Sopenharmony_ci runtime->private_free = snd_card_asihpi_runtime_free; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci memset(&snd_card_asihpi_capture, 0, sizeof(snd_card_asihpi_capture)); 11868c2ecf20Sopenharmony_ci if (!card->hpi->interrupt_mode) { 11878c2ecf20Sopenharmony_ci snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX; 11888c2ecf20Sopenharmony_ci snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN; 11898c2ecf20Sopenharmony_ci snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; 11908c2ecf20Sopenharmony_ci snd_card_asihpi_capture.periods_min = PERIODS_MIN; 11918c2ecf20Sopenharmony_ci snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN; 11928c2ecf20Sopenharmony_ci } else { 11938c2ecf20Sopenharmony_ci size_t pbmin = card->update_interval_frames * 11948c2ecf20Sopenharmony_ci card->out_max_chans; 11958c2ecf20Sopenharmony_ci snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX; 11968c2ecf20Sopenharmony_ci snd_card_asihpi_capture.period_bytes_min = pbmin; 11978c2ecf20Sopenharmony_ci snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; 11988c2ecf20Sopenharmony_ci snd_card_asihpi_capture.periods_min = PERIODS_MIN; 11998c2ecf20Sopenharmony_ci snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / pbmin; 12008c2ecf20Sopenharmony_ci } 12018c2ecf20Sopenharmony_ci /* snd_card_asihpi_capture.fifo_size = 0; */ 12028c2ecf20Sopenharmony_ci snd_card_asihpi_capture.channels_max = card->in_max_chans; 12038c2ecf20Sopenharmony_ci snd_card_asihpi_capture.channels_min = card->in_min_chans; 12048c2ecf20Sopenharmony_ci snd_card_asihpi_capture.formats = 12058c2ecf20Sopenharmony_ci snd_card_asihpi_capture_formats(card, dpcm->h_stream); 12068c2ecf20Sopenharmony_ci snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_capture); 12078c2ecf20Sopenharmony_ci snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED | 12088c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 12098c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci if (card->support_grouping) 12128c2ecf20Sopenharmony_ci snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START; 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci runtime->hw = snd_card_asihpi_capture; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci if (card->can_dma) 12178c2ecf20Sopenharmony_ci err = snd_pcm_hw_constraint_pow2(runtime, 0, 12188c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_BYTES); 12198c2ecf20Sopenharmony_ci if (err < 0) 12208c2ecf20Sopenharmony_ci return err; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 12238c2ecf20Sopenharmony_ci card->update_interval_frames); 12248c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 12258c2ecf20Sopenharmony_ci card->update_interval_frames, UINT_MAX); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci snd_pcm_set_sync(substream); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci return 0; 12308c2ecf20Sopenharmony_ci} 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_cistatic int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream) 12338c2ecf20Sopenharmony_ci{ 12348c2ecf20Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data; 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci hpi_handle_error(hpi_instream_close(dpcm->h_stream)); 12378c2ecf20Sopenharmony_ci return 0; 12388c2ecf20Sopenharmony_ci} 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = { 12418c2ecf20Sopenharmony_ci .open = snd_card_asihpi_capture_open, 12428c2ecf20Sopenharmony_ci .close = snd_card_asihpi_capture_close, 12438c2ecf20Sopenharmony_ci .hw_params = snd_card_asihpi_pcm_hw_params, 12448c2ecf20Sopenharmony_ci .hw_free = snd_card_asihpi_hw_free, 12458c2ecf20Sopenharmony_ci .prepare = snd_card_asihpi_capture_prepare, 12468c2ecf20Sopenharmony_ci .trigger = snd_card_asihpi_trigger, 12478c2ecf20Sopenharmony_ci .pointer = snd_card_asihpi_capture_pointer, 12488c2ecf20Sopenharmony_ci}; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_cistatic int snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, int device) 12518c2ecf20Sopenharmony_ci{ 12528c2ecf20Sopenharmony_ci struct snd_pcm *pcm; 12538c2ecf20Sopenharmony_ci int err; 12548c2ecf20Sopenharmony_ci u16 num_instreams, num_outstreams, x16; 12558c2ecf20Sopenharmony_ci u32 x32; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci err = hpi_adapter_get_info(asihpi->hpi->adapter->index, 12588c2ecf20Sopenharmony_ci &num_outstreams, &num_instreams, 12598c2ecf20Sopenharmony_ci &x16, &x32, &x16); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci err = snd_pcm_new(asihpi->card, "Asihpi PCM", device, 12628c2ecf20Sopenharmony_ci num_outstreams, num_instreams, &pcm); 12638c2ecf20Sopenharmony_ci if (err < 0) 12648c2ecf20Sopenharmony_ci return err; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci /* pointer to ops struct is stored, dont change ops afterwards! */ 12678c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 12688c2ecf20Sopenharmony_ci &snd_card_asihpi_playback_mmap_ops); 12698c2ecf20Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, 12708c2ecf20Sopenharmony_ci &snd_card_asihpi_capture_mmap_ops); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci pcm->private_data = asihpi; 12738c2ecf20Sopenharmony_ci pcm->info_flags = 0; 12748c2ecf20Sopenharmony_ci strcpy(pcm->name, "Asihpi PCM"); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci /*? do we want to emulate MMAP for non-BBM cards? 12778c2ecf20Sopenharmony_ci Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */ 12788c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 12798c2ecf20Sopenharmony_ci &asihpi->pci->dev, 12808c2ecf20Sopenharmony_ci 64*1024, BUFFER_BYTES_MAX); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci return 0; 12838c2ecf20Sopenharmony_ci} 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci/***************************** MIXER CONTROLS ****************/ 12868c2ecf20Sopenharmony_cistruct hpi_control { 12878c2ecf20Sopenharmony_ci u32 h_control; 12888c2ecf20Sopenharmony_ci u16 control_type; 12898c2ecf20Sopenharmony_ci u16 src_node_type; 12908c2ecf20Sopenharmony_ci u16 src_node_index; 12918c2ecf20Sopenharmony_ci u16 dst_node_type; 12928c2ecf20Sopenharmony_ci u16 dst_node_index; 12938c2ecf20Sopenharmony_ci u16 band; 12948c2ecf20Sopenharmony_ci char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* copied to snd_ctl_elem_id.name[44]; */ 12958c2ecf20Sopenharmony_ci}; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cistatic const char * const asihpi_tuner_band_names[] = { 12988c2ecf20Sopenharmony_ci "invalid", 12998c2ecf20Sopenharmony_ci "AM", 13008c2ecf20Sopenharmony_ci "FM mono", 13018c2ecf20Sopenharmony_ci "TV NTSC-M", 13028c2ecf20Sopenharmony_ci "FM stereo", 13038c2ecf20Sopenharmony_ci "AUX", 13048c2ecf20Sopenharmony_ci "TV PAL BG", 13058c2ecf20Sopenharmony_ci "TV PAL I", 13068c2ecf20Sopenharmony_ci "TV PAL DK", 13078c2ecf20Sopenharmony_ci "TV SECAM", 13088c2ecf20Sopenharmony_ci "TV DAB", 13098c2ecf20Sopenharmony_ci}; 13108c2ecf20Sopenharmony_ci/* Number of strings must match the enumerations for HPI_TUNER_BAND in hpi.h */ 13118c2ecf20Sopenharmony_cicompile_time_assert( 13128c2ecf20Sopenharmony_ci (ARRAY_SIZE(asihpi_tuner_band_names) == 13138c2ecf20Sopenharmony_ci (HPI_TUNER_BAND_LAST+1)), 13148c2ecf20Sopenharmony_ci assert_tuner_band_names_size); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_cistatic const char * const asihpi_src_names[] = { 13178c2ecf20Sopenharmony_ci "no source", 13188c2ecf20Sopenharmony_ci "PCM", 13198c2ecf20Sopenharmony_ci "Line", 13208c2ecf20Sopenharmony_ci "Digital", 13218c2ecf20Sopenharmony_ci "Tuner", 13228c2ecf20Sopenharmony_ci "RF", 13238c2ecf20Sopenharmony_ci "Clock", 13248c2ecf20Sopenharmony_ci "Bitstream", 13258c2ecf20Sopenharmony_ci "Mic", 13268c2ecf20Sopenharmony_ci "Net", 13278c2ecf20Sopenharmony_ci "Analog", 13288c2ecf20Sopenharmony_ci "Adapter", 13298c2ecf20Sopenharmony_ci "RTP", 13308c2ecf20Sopenharmony_ci "Internal", 13318c2ecf20Sopenharmony_ci "AVB", 13328c2ecf20Sopenharmony_ci "BLU-Link" 13338c2ecf20Sopenharmony_ci}; 13348c2ecf20Sopenharmony_ci/* Number of strings must match the enumerations for HPI_SOURCENODES in hpi.h */ 13358c2ecf20Sopenharmony_cicompile_time_assert( 13368c2ecf20Sopenharmony_ci (ARRAY_SIZE(asihpi_src_names) == 13378c2ecf20Sopenharmony_ci (HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_NONE+1)), 13388c2ecf20Sopenharmony_ci assert_src_names_size); 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cistatic const char * const asihpi_dst_names[] = { 13418c2ecf20Sopenharmony_ci "no destination", 13428c2ecf20Sopenharmony_ci "PCM", 13438c2ecf20Sopenharmony_ci "Line", 13448c2ecf20Sopenharmony_ci "Digital", 13458c2ecf20Sopenharmony_ci "RF", 13468c2ecf20Sopenharmony_ci "Speaker", 13478c2ecf20Sopenharmony_ci "Net", 13488c2ecf20Sopenharmony_ci "Analog", 13498c2ecf20Sopenharmony_ci "RTP", 13508c2ecf20Sopenharmony_ci "AVB", 13518c2ecf20Sopenharmony_ci "Internal", 13528c2ecf20Sopenharmony_ci "BLU-Link" 13538c2ecf20Sopenharmony_ci}; 13548c2ecf20Sopenharmony_ci/* Number of strings must match the enumerations for HPI_DESTNODES in hpi.h */ 13558c2ecf20Sopenharmony_cicompile_time_assert( 13568c2ecf20Sopenharmony_ci (ARRAY_SIZE(asihpi_dst_names) == 13578c2ecf20Sopenharmony_ci (HPI_DESTNODE_LAST_INDEX-HPI_DESTNODE_NONE+1)), 13588c2ecf20Sopenharmony_ci assert_dst_names_size); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_cistatic inline int ctl_add(struct snd_card *card, struct snd_kcontrol_new *ctl, 13618c2ecf20Sopenharmony_ci struct snd_card_asihpi *asihpi) 13628c2ecf20Sopenharmony_ci{ 13638c2ecf20Sopenharmony_ci int err; 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(ctl, asihpi)); 13668c2ecf20Sopenharmony_ci if (err < 0) 13678c2ecf20Sopenharmony_ci return err; 13688c2ecf20Sopenharmony_ci else if (mixer_dump) 13698c2ecf20Sopenharmony_ci dev_info(&asihpi->pci->dev, "added %s(%d)\n", ctl->name, ctl->index); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci return 0; 13728c2ecf20Sopenharmony_ci} 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci/* Convert HPI control name and location into ALSA control name */ 13758c2ecf20Sopenharmony_cistatic void asihpi_ctl_init(struct snd_kcontrol_new *snd_control, 13768c2ecf20Sopenharmony_ci struct hpi_control *hpi_ctl, 13778c2ecf20Sopenharmony_ci char *name) 13788c2ecf20Sopenharmony_ci{ 13798c2ecf20Sopenharmony_ci char *dir; 13808c2ecf20Sopenharmony_ci memset(snd_control, 0, sizeof(*snd_control)); 13818c2ecf20Sopenharmony_ci snd_control->name = hpi_ctl->name; 13828c2ecf20Sopenharmony_ci snd_control->private_value = hpi_ctl->h_control; 13838c2ecf20Sopenharmony_ci snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 13848c2ecf20Sopenharmony_ci snd_control->index = 0; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci if (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE == HPI_SOURCENODE_CLOCK_SOURCE) 13878c2ecf20Sopenharmony_ci dir = ""; /* clock is neither capture nor playback */ 13888c2ecf20Sopenharmony_ci else if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM) 13898c2ecf20Sopenharmony_ci dir = "Capture "; /* On or towards a PCM capture destination*/ 13908c2ecf20Sopenharmony_ci else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) && 13918c2ecf20Sopenharmony_ci (!hpi_ctl->dst_node_type)) 13928c2ecf20Sopenharmony_ci dir = "Capture "; /* On a source node that is not PCM playback */ 13938c2ecf20Sopenharmony_ci else if (hpi_ctl->src_node_type && 13948c2ecf20Sopenharmony_ci (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) && 13958c2ecf20Sopenharmony_ci (hpi_ctl->dst_node_type)) 13968c2ecf20Sopenharmony_ci dir = "Monitor Playback "; /* Between an input and an output */ 13978c2ecf20Sopenharmony_ci else 13988c2ecf20Sopenharmony_ci dir = "Playback "; /* PCM Playback source, or output node */ 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type) 14018c2ecf20Sopenharmony_ci sprintf(hpi_ctl->name, "%s %d %s %d %s%s", 14028c2ecf20Sopenharmony_ci asihpi_src_names[hpi_ctl->src_node_type], 14038c2ecf20Sopenharmony_ci hpi_ctl->src_node_index, 14048c2ecf20Sopenharmony_ci asihpi_dst_names[hpi_ctl->dst_node_type], 14058c2ecf20Sopenharmony_ci hpi_ctl->dst_node_index, 14068c2ecf20Sopenharmony_ci dir, name); 14078c2ecf20Sopenharmony_ci else if (hpi_ctl->dst_node_type) { 14088c2ecf20Sopenharmony_ci sprintf(hpi_ctl->name, "%s %d %s%s", 14098c2ecf20Sopenharmony_ci asihpi_dst_names[hpi_ctl->dst_node_type], 14108c2ecf20Sopenharmony_ci hpi_ctl->dst_node_index, 14118c2ecf20Sopenharmony_ci dir, name); 14128c2ecf20Sopenharmony_ci } else { 14138c2ecf20Sopenharmony_ci sprintf(hpi_ctl->name, "%s %d %s%s", 14148c2ecf20Sopenharmony_ci asihpi_src_names[hpi_ctl->src_node_type], 14158c2ecf20Sopenharmony_ci hpi_ctl->src_node_index, 14168c2ecf20Sopenharmony_ci dir, name); 14178c2ecf20Sopenharmony_ci } 14188c2ecf20Sopenharmony_ci /* printk(KERN_INFO "Adding %s %d to %d ", hpi_ctl->name, 14198c2ecf20Sopenharmony_ci hpi_ctl->wSrcNodeType, hpi_ctl->wDstNodeType); */ 14208c2ecf20Sopenharmony_ci} 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci/*------------------------------------------------------------ 14238c2ecf20Sopenharmony_ci Volume controls 14248c2ecf20Sopenharmony_ci ------------------------------------------------------------*/ 14258c2ecf20Sopenharmony_ci#define VOL_STEP_mB 1 14268c2ecf20Sopenharmony_cistatic int snd_asihpi_volume_info(struct snd_kcontrol *kcontrol, 14278c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 14288c2ecf20Sopenharmony_ci{ 14298c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 14308c2ecf20Sopenharmony_ci u32 count; 14318c2ecf20Sopenharmony_ci u16 err; 14328c2ecf20Sopenharmony_ci /* native gains are in millibels */ 14338c2ecf20Sopenharmony_ci short min_gain_mB; 14348c2ecf20Sopenharmony_ci short max_gain_mB; 14358c2ecf20Sopenharmony_ci short step_gain_mB; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci err = hpi_volume_query_range(h_control, 14388c2ecf20Sopenharmony_ci &min_gain_mB, &max_gain_mB, &step_gain_mB); 14398c2ecf20Sopenharmony_ci if (err) { 14408c2ecf20Sopenharmony_ci max_gain_mB = 0; 14418c2ecf20Sopenharmony_ci min_gain_mB = -10000; 14428c2ecf20Sopenharmony_ci step_gain_mB = VOL_STEP_mB; 14438c2ecf20Sopenharmony_ci } 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci err = hpi_meter_query_channels(h_control, &count); 14468c2ecf20Sopenharmony_ci if (err) 14478c2ecf20Sopenharmony_ci count = HPI_MAX_CHANNELS; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 14508c2ecf20Sopenharmony_ci uinfo->count = count; 14518c2ecf20Sopenharmony_ci uinfo->value.integer.min = min_gain_mB / VOL_STEP_mB; 14528c2ecf20Sopenharmony_ci uinfo->value.integer.max = max_gain_mB / VOL_STEP_mB; 14538c2ecf20Sopenharmony_ci uinfo->value.integer.step = step_gain_mB / VOL_STEP_mB; 14548c2ecf20Sopenharmony_ci return 0; 14558c2ecf20Sopenharmony_ci} 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_cistatic int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol, 14588c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 14598c2ecf20Sopenharmony_ci{ 14608c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 14618c2ecf20Sopenharmony_ci short an_gain_mB[HPI_MAX_CHANNELS]; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci hpi_handle_error(hpi_volume_get_gain(h_control, an_gain_mB)); 14648c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = an_gain_mB[0] / VOL_STEP_mB; 14658c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = an_gain_mB[1] / VOL_STEP_mB; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci return 0; 14688c2ecf20Sopenharmony_ci} 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_cistatic int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol, 14718c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 14728c2ecf20Sopenharmony_ci{ 14738c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 14748c2ecf20Sopenharmony_ci short an_gain_mB[HPI_MAX_CHANNELS]; 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci an_gain_mB[0] = 14778c2ecf20Sopenharmony_ci (ucontrol->value.integer.value[0]) * VOL_STEP_mB; 14788c2ecf20Sopenharmony_ci an_gain_mB[1] = 14798c2ecf20Sopenharmony_ci (ucontrol->value.integer.value[1]) * VOL_STEP_mB; 14808c2ecf20Sopenharmony_ci /* change = asihpi->mixer_volume[addr][0] != left || 14818c2ecf20Sopenharmony_ci asihpi->mixer_volume[addr][1] != right; 14828c2ecf20Sopenharmony_ci */ 14838c2ecf20Sopenharmony_ci hpi_handle_error(hpi_volume_set_gain(h_control, an_gain_mB)); 14848c2ecf20Sopenharmony_ci return 1; 14858c2ecf20Sopenharmony_ci} 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci#define snd_asihpi_volume_mute_info snd_ctl_boolean_mono_info 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_cistatic int snd_asihpi_volume_mute_get(struct snd_kcontrol *kcontrol, 14928c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 14938c2ecf20Sopenharmony_ci{ 14948c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 14958c2ecf20Sopenharmony_ci u32 mute; 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_ci hpi_handle_error(hpi_volume_get_mute(h_control, &mute)); 14988c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = mute ? 0 : 1; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci return 0; 15018c2ecf20Sopenharmony_ci} 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_cistatic int snd_asihpi_volume_mute_put(struct snd_kcontrol *kcontrol, 15048c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 15058c2ecf20Sopenharmony_ci{ 15068c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 15078c2ecf20Sopenharmony_ci /* HPI currently only supports all or none muting of multichannel volume 15088c2ecf20Sopenharmony_ci ALSA Switch element has opposite sense to HPI mute: on==unmuted, off=muted 15098c2ecf20Sopenharmony_ci */ 15108c2ecf20Sopenharmony_ci int mute = ucontrol->value.integer.value[0] ? 0 : HPI_BITMASK_ALL_CHANNELS; 15118c2ecf20Sopenharmony_ci hpi_handle_error(hpi_volume_set_mute(h_control, mute)); 15128c2ecf20Sopenharmony_ci return 1; 15138c2ecf20Sopenharmony_ci} 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_cistatic int snd_asihpi_volume_add(struct snd_card_asihpi *asihpi, 15168c2ecf20Sopenharmony_ci struct hpi_control *hpi_ctl) 15178c2ecf20Sopenharmony_ci{ 15188c2ecf20Sopenharmony_ci struct snd_card *card = asihpi->card; 15198c2ecf20Sopenharmony_ci struct snd_kcontrol_new snd_control; 15208c2ecf20Sopenharmony_ci int err; 15218c2ecf20Sopenharmony_ci u32 mute; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Volume"); 15248c2ecf20Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 15258c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ; 15268c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_volume_info; 15278c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_volume_get; 15288c2ecf20Sopenharmony_ci snd_control.put = snd_asihpi_volume_put; 15298c2ecf20Sopenharmony_ci snd_control.tlv.p = db_scale_100; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci err = ctl_add(card, &snd_control, asihpi); 15328c2ecf20Sopenharmony_ci if (err) 15338c2ecf20Sopenharmony_ci return err; 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci if (hpi_volume_get_mute(hpi_ctl->h_control, &mute) == 0) { 15368c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Switch"); 15378c2ecf20Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 15388c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_volume_mute_info; 15398c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_volume_mute_get; 15408c2ecf20Sopenharmony_ci snd_control.put = snd_asihpi_volume_mute_put; 15418c2ecf20Sopenharmony_ci err = ctl_add(card, &snd_control, asihpi); 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci return err; 15448c2ecf20Sopenharmony_ci} 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci/*------------------------------------------------------------ 15478c2ecf20Sopenharmony_ci Level controls 15488c2ecf20Sopenharmony_ci ------------------------------------------------------------*/ 15498c2ecf20Sopenharmony_cistatic int snd_asihpi_level_info(struct snd_kcontrol *kcontrol, 15508c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 15518c2ecf20Sopenharmony_ci{ 15528c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 15538c2ecf20Sopenharmony_ci u16 err; 15548c2ecf20Sopenharmony_ci short min_gain_mB; 15558c2ecf20Sopenharmony_ci short max_gain_mB; 15568c2ecf20Sopenharmony_ci short step_gain_mB; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci err = 15598c2ecf20Sopenharmony_ci hpi_level_query_range(h_control, &min_gain_mB, 15608c2ecf20Sopenharmony_ci &max_gain_mB, &step_gain_mB); 15618c2ecf20Sopenharmony_ci if (err) { 15628c2ecf20Sopenharmony_ci max_gain_mB = 2400; 15638c2ecf20Sopenharmony_ci min_gain_mB = -1000; 15648c2ecf20Sopenharmony_ci step_gain_mB = 100; 15658c2ecf20Sopenharmony_ci } 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 15688c2ecf20Sopenharmony_ci uinfo->count = 2; 15698c2ecf20Sopenharmony_ci uinfo->value.integer.min = min_gain_mB / HPI_UNITS_PER_dB; 15708c2ecf20Sopenharmony_ci uinfo->value.integer.max = max_gain_mB / HPI_UNITS_PER_dB; 15718c2ecf20Sopenharmony_ci uinfo->value.integer.step = step_gain_mB / HPI_UNITS_PER_dB; 15728c2ecf20Sopenharmony_ci return 0; 15738c2ecf20Sopenharmony_ci} 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_cistatic int snd_asihpi_level_get(struct snd_kcontrol *kcontrol, 15768c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 15778c2ecf20Sopenharmony_ci{ 15788c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 15798c2ecf20Sopenharmony_ci short an_gain_mB[HPI_MAX_CHANNELS]; 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci hpi_handle_error(hpi_level_get_gain(h_control, an_gain_mB)); 15828c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 15838c2ecf20Sopenharmony_ci an_gain_mB[0] / HPI_UNITS_PER_dB; 15848c2ecf20Sopenharmony_ci ucontrol->value.integer.value[1] = 15858c2ecf20Sopenharmony_ci an_gain_mB[1] / HPI_UNITS_PER_dB; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci return 0; 15888c2ecf20Sopenharmony_ci} 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_cistatic int snd_asihpi_level_put(struct snd_kcontrol *kcontrol, 15918c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 15928c2ecf20Sopenharmony_ci{ 15938c2ecf20Sopenharmony_ci int change; 15948c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 15958c2ecf20Sopenharmony_ci short an_gain_mB[HPI_MAX_CHANNELS]; 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci an_gain_mB[0] = 15988c2ecf20Sopenharmony_ci (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB; 15998c2ecf20Sopenharmony_ci an_gain_mB[1] = 16008c2ecf20Sopenharmony_ci (ucontrol->value.integer.value[1]) * HPI_UNITS_PER_dB; 16018c2ecf20Sopenharmony_ci /* change = asihpi->mixer_level[addr][0] != left || 16028c2ecf20Sopenharmony_ci asihpi->mixer_level[addr][1] != right; 16038c2ecf20Sopenharmony_ci */ 16048c2ecf20Sopenharmony_ci change = 1; 16058c2ecf20Sopenharmony_ci hpi_handle_error(hpi_level_set_gain(h_control, an_gain_mB)); 16068c2ecf20Sopenharmony_ci return change; 16078c2ecf20Sopenharmony_ci} 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_level, -1000, 100, 0); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_cistatic int snd_asihpi_level_add(struct snd_card_asihpi *asihpi, 16128c2ecf20Sopenharmony_ci struct hpi_control *hpi_ctl) 16138c2ecf20Sopenharmony_ci{ 16148c2ecf20Sopenharmony_ci struct snd_card *card = asihpi->card; 16158c2ecf20Sopenharmony_ci struct snd_kcontrol_new snd_control; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci /* can't use 'volume' cos some nodes have volume as well */ 16188c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Level"); 16198c2ecf20Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 16208c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ; 16218c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_level_info; 16228c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_level_get; 16238c2ecf20Sopenharmony_ci snd_control.put = snd_asihpi_level_put; 16248c2ecf20Sopenharmony_ci snd_control.tlv.p = db_scale_level; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 16278c2ecf20Sopenharmony_ci} 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci/*------------------------------------------------------------ 16308c2ecf20Sopenharmony_ci AESEBU controls 16318c2ecf20Sopenharmony_ci ------------------------------------------------------------*/ 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci/* AESEBU format */ 16348c2ecf20Sopenharmony_cistatic const char * const asihpi_aesebu_format_names[] = { 16358c2ecf20Sopenharmony_ci "N/A", "S/PDIF", "AES/EBU" }; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol, 16388c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 16398c2ecf20Sopenharmony_ci{ 16408c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 3, asihpi_aesebu_format_names); 16418c2ecf20Sopenharmony_ci} 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol, 16448c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol, 16458c2ecf20Sopenharmony_ci u16 (*func)(u32, u16 *)) 16468c2ecf20Sopenharmony_ci{ 16478c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 16488c2ecf20Sopenharmony_ci u16 source, err; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci err = func(h_control, &source); 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_ci /* default to N/A */ 16538c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = 0; 16548c2ecf20Sopenharmony_ci /* return success but set the control to N/A */ 16558c2ecf20Sopenharmony_ci if (err) 16568c2ecf20Sopenharmony_ci return 0; 16578c2ecf20Sopenharmony_ci if (source == HPI_AESEBU_FORMAT_SPDIF) 16588c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = 1; 16598c2ecf20Sopenharmony_ci if (source == HPI_AESEBU_FORMAT_AESEBU) 16608c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = 2; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci return 0; 16638c2ecf20Sopenharmony_ci} 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol, 16668c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol, 16678c2ecf20Sopenharmony_ci u16 (*func)(u32, u16)) 16688c2ecf20Sopenharmony_ci{ 16698c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci /* default to S/PDIF */ 16728c2ecf20Sopenharmony_ci u16 source = HPI_AESEBU_FORMAT_SPDIF; 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci if (ucontrol->value.enumerated.item[0] == 1) 16758c2ecf20Sopenharmony_ci source = HPI_AESEBU_FORMAT_SPDIF; 16768c2ecf20Sopenharmony_ci if (ucontrol->value.enumerated.item[0] == 2) 16778c2ecf20Sopenharmony_ci source = HPI_AESEBU_FORMAT_AESEBU; 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci if (func(h_control, source) != 0) 16808c2ecf20Sopenharmony_ci return -EINVAL; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci return 1; 16838c2ecf20Sopenharmony_ci} 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_rx_format_get(struct snd_kcontrol *kcontrol, 16868c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) { 16878c2ecf20Sopenharmony_ci return snd_asihpi_aesebu_format_get(kcontrol, ucontrol, 16888c2ecf20Sopenharmony_ci hpi_aesebu_receiver_get_format); 16898c2ecf20Sopenharmony_ci} 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_rx_format_put(struct snd_kcontrol *kcontrol, 16928c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) { 16938c2ecf20Sopenharmony_ci return snd_asihpi_aesebu_format_put(kcontrol, ucontrol, 16948c2ecf20Sopenharmony_ci hpi_aesebu_receiver_set_format); 16958c2ecf20Sopenharmony_ci} 16968c2ecf20Sopenharmony_ci 16978c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_rxstatus_info(struct snd_kcontrol *kcontrol, 16988c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 16998c2ecf20Sopenharmony_ci{ 17008c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 17018c2ecf20Sopenharmony_ci uinfo->count = 1; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 17048c2ecf20Sopenharmony_ci uinfo->value.integer.max = 0X1F; 17058c2ecf20Sopenharmony_ci uinfo->value.integer.step = 1; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci return 0; 17088c2ecf20Sopenharmony_ci} 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_rxstatus_get(struct snd_kcontrol *kcontrol, 17118c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) { 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 17148c2ecf20Sopenharmony_ci u16 status; 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci hpi_handle_error(hpi_aesebu_receiver_get_error_status( 17178c2ecf20Sopenharmony_ci h_control, &status)); 17188c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = status; 17198c2ecf20Sopenharmony_ci return 0; 17208c2ecf20Sopenharmony_ci} 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi, 17238c2ecf20Sopenharmony_ci struct hpi_control *hpi_ctl) 17248c2ecf20Sopenharmony_ci{ 17258c2ecf20Sopenharmony_ci struct snd_card *card = asihpi->card; 17268c2ecf20Sopenharmony_ci struct snd_kcontrol_new snd_control; 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Format"); 17298c2ecf20Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 17308c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_aesebu_format_info; 17318c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_aesebu_rx_format_get; 17328c2ecf20Sopenharmony_ci snd_control.put = snd_asihpi_aesebu_rx_format_put; 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_ci if (ctl_add(card, &snd_control, asihpi) < 0) 17368c2ecf20Sopenharmony_ci return -EINVAL; 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Status"); 17398c2ecf20Sopenharmony_ci snd_control.access = 17408c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; 17418c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_aesebu_rxstatus_info; 17428c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_aesebu_rxstatus_get; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 17458c2ecf20Sopenharmony_ci} 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_tx_format_get(struct snd_kcontrol *kcontrol, 17488c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) { 17498c2ecf20Sopenharmony_ci return snd_asihpi_aesebu_format_get(kcontrol, ucontrol, 17508c2ecf20Sopenharmony_ci hpi_aesebu_transmitter_get_format); 17518c2ecf20Sopenharmony_ci} 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_tx_format_put(struct snd_kcontrol *kcontrol, 17548c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) { 17558c2ecf20Sopenharmony_ci return snd_asihpi_aesebu_format_put(kcontrol, ucontrol, 17568c2ecf20Sopenharmony_ci hpi_aesebu_transmitter_set_format); 17578c2ecf20Sopenharmony_ci} 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_cistatic int snd_asihpi_aesebu_tx_add(struct snd_card_asihpi *asihpi, 17618c2ecf20Sopenharmony_ci struct hpi_control *hpi_ctl) 17628c2ecf20Sopenharmony_ci{ 17638c2ecf20Sopenharmony_ci struct snd_card *card = asihpi->card; 17648c2ecf20Sopenharmony_ci struct snd_kcontrol_new snd_control; 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Format"); 17678c2ecf20Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 17688c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_aesebu_format_info; 17698c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_aesebu_tx_format_get; 17708c2ecf20Sopenharmony_ci snd_control.put = snd_asihpi_aesebu_tx_format_put; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 17738c2ecf20Sopenharmony_ci} 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci/*------------------------------------------------------------ 17768c2ecf20Sopenharmony_ci Tuner controls 17778c2ecf20Sopenharmony_ci ------------------------------------------------------------*/ 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci/* Gain */ 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_gain_info(struct snd_kcontrol *kcontrol, 17828c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 17838c2ecf20Sopenharmony_ci{ 17848c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 17858c2ecf20Sopenharmony_ci u16 err; 17868c2ecf20Sopenharmony_ci short idx; 17878c2ecf20Sopenharmony_ci u16 gain_range[3]; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci for (idx = 0; idx < 3; idx++) { 17908c2ecf20Sopenharmony_ci err = hpi_tuner_query_gain(h_control, 17918c2ecf20Sopenharmony_ci idx, &gain_range[idx]); 17928c2ecf20Sopenharmony_ci if (err != 0) 17938c2ecf20Sopenharmony_ci return err; 17948c2ecf20Sopenharmony_ci } 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 17978c2ecf20Sopenharmony_ci uinfo->count = 1; 17988c2ecf20Sopenharmony_ci uinfo->value.integer.min = ((int)gain_range[0]) / HPI_UNITS_PER_dB; 17998c2ecf20Sopenharmony_ci uinfo->value.integer.max = ((int)gain_range[1]) / HPI_UNITS_PER_dB; 18008c2ecf20Sopenharmony_ci uinfo->value.integer.step = ((int) gain_range[2]) / HPI_UNITS_PER_dB; 18018c2ecf20Sopenharmony_ci return 0; 18028c2ecf20Sopenharmony_ci} 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_gain_get(struct snd_kcontrol *kcontrol, 18058c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 18068c2ecf20Sopenharmony_ci{ 18078c2ecf20Sopenharmony_ci /* 18088c2ecf20Sopenharmony_ci struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); 18098c2ecf20Sopenharmony_ci */ 18108c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 18118c2ecf20Sopenharmony_ci short gain; 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci hpi_handle_error(hpi_tuner_get_gain(h_control, &gain)); 18148c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = gain / HPI_UNITS_PER_dB; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci return 0; 18178c2ecf20Sopenharmony_ci} 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_gain_put(struct snd_kcontrol *kcontrol, 18208c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 18218c2ecf20Sopenharmony_ci{ 18228c2ecf20Sopenharmony_ci /* 18238c2ecf20Sopenharmony_ci struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); 18248c2ecf20Sopenharmony_ci */ 18258c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 18268c2ecf20Sopenharmony_ci short gain; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci gain = (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB; 18298c2ecf20Sopenharmony_ci hpi_handle_error(hpi_tuner_set_gain(h_control, gain)); 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci return 1; 18328c2ecf20Sopenharmony_ci} 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci/* Band */ 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_cistatic int asihpi_tuner_band_query(struct snd_kcontrol *kcontrol, 18378c2ecf20Sopenharmony_ci u16 *band_list, u32 len) { 18388c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 18398c2ecf20Sopenharmony_ci u16 err = 0; 18408c2ecf20Sopenharmony_ci u32 i; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 18438c2ecf20Sopenharmony_ci err = hpi_tuner_query_band( 18448c2ecf20Sopenharmony_ci h_control, i, &band_list[i]); 18458c2ecf20Sopenharmony_ci if (err != 0) 18468c2ecf20Sopenharmony_ci break; 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci if (err && (err != HPI_ERROR_INVALID_OBJ_INDEX)) 18508c2ecf20Sopenharmony_ci return -EIO; 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci return i; 18538c2ecf20Sopenharmony_ci} 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_band_info(struct snd_kcontrol *kcontrol, 18568c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 18578c2ecf20Sopenharmony_ci{ 18588c2ecf20Sopenharmony_ci u16 tuner_bands[HPI_TUNER_BAND_LAST]; 18598c2ecf20Sopenharmony_ci int num_bands = 0; 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, 18628c2ecf20Sopenharmony_ci HPI_TUNER_BAND_LAST); 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci if (num_bands < 0) 18658c2ecf20Sopenharmony_ci return num_bands; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, num_bands, asihpi_tuner_band_names); 18688c2ecf20Sopenharmony_ci} 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol, 18718c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 18728c2ecf20Sopenharmony_ci{ 18738c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 18748c2ecf20Sopenharmony_ci /* 18758c2ecf20Sopenharmony_ci struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); 18768c2ecf20Sopenharmony_ci */ 18778c2ecf20Sopenharmony_ci u16 band, idx; 18788c2ecf20Sopenharmony_ci u16 tuner_bands[HPI_TUNER_BAND_LAST]; 18798c2ecf20Sopenharmony_ci __always_unused u32 num_bands; 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, 18828c2ecf20Sopenharmony_ci HPI_TUNER_BAND_LAST); 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci hpi_handle_error(hpi_tuner_get_band(h_control, &band)); 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = -1; 18878c2ecf20Sopenharmony_ci for (idx = 0; idx < HPI_TUNER_BAND_LAST; idx++) 18888c2ecf20Sopenharmony_ci if (tuner_bands[idx] == band) { 18898c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = idx; 18908c2ecf20Sopenharmony_ci break; 18918c2ecf20Sopenharmony_ci } 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci return 0; 18948c2ecf20Sopenharmony_ci} 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol, 18978c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 18988c2ecf20Sopenharmony_ci{ 18998c2ecf20Sopenharmony_ci /* 19008c2ecf20Sopenharmony_ci struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); 19018c2ecf20Sopenharmony_ci */ 19028c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 19038c2ecf20Sopenharmony_ci unsigned int idx; 19048c2ecf20Sopenharmony_ci u16 band; 19058c2ecf20Sopenharmony_ci u16 tuner_bands[HPI_TUNER_BAND_LAST]; 19068c2ecf20Sopenharmony_ci __always_unused u32 num_bands; 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, 19098c2ecf20Sopenharmony_ci HPI_TUNER_BAND_LAST); 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci idx = ucontrol->value.enumerated.item[0]; 19128c2ecf20Sopenharmony_ci if (idx >= ARRAY_SIZE(tuner_bands)) 19138c2ecf20Sopenharmony_ci idx = ARRAY_SIZE(tuner_bands) - 1; 19148c2ecf20Sopenharmony_ci band = tuner_bands[idx]; 19158c2ecf20Sopenharmony_ci hpi_handle_error(hpi_tuner_set_band(h_control, band)); 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci return 1; 19188c2ecf20Sopenharmony_ci} 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci/* Freq */ 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_freq_info(struct snd_kcontrol *kcontrol, 19238c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 19248c2ecf20Sopenharmony_ci{ 19258c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 19268c2ecf20Sopenharmony_ci u16 err; 19278c2ecf20Sopenharmony_ci u16 tuner_bands[HPI_TUNER_BAND_LAST]; 19288c2ecf20Sopenharmony_ci u16 num_bands = 0, band_iter, idx; 19298c2ecf20Sopenharmony_ci u32 freq_range[3], temp_freq_range[3]; 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, 19328c2ecf20Sopenharmony_ci HPI_TUNER_BAND_LAST); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci freq_range[0] = INT_MAX; 19358c2ecf20Sopenharmony_ci freq_range[1] = 0; 19368c2ecf20Sopenharmony_ci freq_range[2] = INT_MAX; 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci for (band_iter = 0; band_iter < num_bands; band_iter++) { 19398c2ecf20Sopenharmony_ci for (idx = 0; idx < 3; idx++) { 19408c2ecf20Sopenharmony_ci err = hpi_tuner_query_frequency(h_control, 19418c2ecf20Sopenharmony_ci idx, tuner_bands[band_iter], 19428c2ecf20Sopenharmony_ci &temp_freq_range[idx]); 19438c2ecf20Sopenharmony_ci if (err != 0) 19448c2ecf20Sopenharmony_ci return err; 19458c2ecf20Sopenharmony_ci } 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci /* skip band with bogus stepping */ 19488c2ecf20Sopenharmony_ci if (temp_freq_range[2] <= 0) 19498c2ecf20Sopenharmony_ci continue; 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_ci if (temp_freq_range[0] < freq_range[0]) 19528c2ecf20Sopenharmony_ci freq_range[0] = temp_freq_range[0]; 19538c2ecf20Sopenharmony_ci if (temp_freq_range[1] > freq_range[1]) 19548c2ecf20Sopenharmony_ci freq_range[1] = temp_freq_range[1]; 19558c2ecf20Sopenharmony_ci if (temp_freq_range[2] < freq_range[2]) 19568c2ecf20Sopenharmony_ci freq_range[2] = temp_freq_range[2]; 19578c2ecf20Sopenharmony_ci } 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 19608c2ecf20Sopenharmony_ci uinfo->count = 1; 19618c2ecf20Sopenharmony_ci uinfo->value.integer.min = ((int)freq_range[0]); 19628c2ecf20Sopenharmony_ci uinfo->value.integer.max = ((int)freq_range[1]); 19638c2ecf20Sopenharmony_ci uinfo->value.integer.step = ((int)freq_range[2]); 19648c2ecf20Sopenharmony_ci return 0; 19658c2ecf20Sopenharmony_ci} 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_freq_get(struct snd_kcontrol *kcontrol, 19688c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 19698c2ecf20Sopenharmony_ci{ 19708c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 19718c2ecf20Sopenharmony_ci u32 freq; 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci hpi_handle_error(hpi_tuner_get_frequency(h_control, &freq)); 19748c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = freq; 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci return 0; 19778c2ecf20Sopenharmony_ci} 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_freq_put(struct snd_kcontrol *kcontrol, 19808c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 19818c2ecf20Sopenharmony_ci{ 19828c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 19838c2ecf20Sopenharmony_ci u32 freq; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci freq = ucontrol->value.integer.value[0]; 19868c2ecf20Sopenharmony_ci hpi_handle_error(hpi_tuner_set_frequency(h_control, freq)); 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci return 1; 19898c2ecf20Sopenharmony_ci} 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci/* Tuner control group initializer */ 19928c2ecf20Sopenharmony_cistatic int snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi, 19938c2ecf20Sopenharmony_ci struct hpi_control *hpi_ctl) 19948c2ecf20Sopenharmony_ci{ 19958c2ecf20Sopenharmony_ci struct snd_card *card = asihpi->card; 19968c2ecf20Sopenharmony_ci struct snd_kcontrol_new snd_control; 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci snd_control.private_value = hpi_ctl->h_control; 19998c2ecf20Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci if (!hpi_tuner_get_gain(hpi_ctl->h_control, NULL)) { 20028c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Gain"); 20038c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_tuner_gain_info; 20048c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_tuner_gain_get; 20058c2ecf20Sopenharmony_ci snd_control.put = snd_asihpi_tuner_gain_put; 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci if (ctl_add(card, &snd_control, asihpi) < 0) 20088c2ecf20Sopenharmony_ci return -EINVAL; 20098c2ecf20Sopenharmony_ci } 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Band"); 20128c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_tuner_band_info; 20138c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_tuner_band_get; 20148c2ecf20Sopenharmony_ci snd_control.put = snd_asihpi_tuner_band_put; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci if (ctl_add(card, &snd_control, asihpi) < 0) 20178c2ecf20Sopenharmony_ci return -EINVAL; 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Freq"); 20208c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_tuner_freq_info; 20218c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_tuner_freq_get; 20228c2ecf20Sopenharmony_ci snd_control.put = snd_asihpi_tuner_freq_put; 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 20258c2ecf20Sopenharmony_ci} 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci/*------------------------------------------------------------ 20288c2ecf20Sopenharmony_ci Meter controls 20298c2ecf20Sopenharmony_ci ------------------------------------------------------------*/ 20308c2ecf20Sopenharmony_cistatic int snd_asihpi_meter_info(struct snd_kcontrol *kcontrol, 20318c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 20328c2ecf20Sopenharmony_ci{ 20338c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 20348c2ecf20Sopenharmony_ci u32 count; 20358c2ecf20Sopenharmony_ci u16 err; 20368c2ecf20Sopenharmony_ci err = hpi_meter_query_channels(h_control, &count); 20378c2ecf20Sopenharmony_ci if (err) 20388c2ecf20Sopenharmony_ci count = HPI_MAX_CHANNELS; 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 20418c2ecf20Sopenharmony_ci uinfo->count = count; 20428c2ecf20Sopenharmony_ci uinfo->value.integer.min = 0; 20438c2ecf20Sopenharmony_ci uinfo->value.integer.max = 0x7FFFFFFF; 20448c2ecf20Sopenharmony_ci return 0; 20458c2ecf20Sopenharmony_ci} 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci/* linear values for 10dB steps */ 20488c2ecf20Sopenharmony_cistatic const int log2lin[] = { 20498c2ecf20Sopenharmony_ci 0x7FFFFFFF, /* 0dB */ 20508c2ecf20Sopenharmony_ci 679093956, 20518c2ecf20Sopenharmony_ci 214748365, 20528c2ecf20Sopenharmony_ci 67909396, 20538c2ecf20Sopenharmony_ci 21474837, 20548c2ecf20Sopenharmony_ci 6790940, 20558c2ecf20Sopenharmony_ci 2147484, /* -60dB */ 20568c2ecf20Sopenharmony_ci 679094, 20578c2ecf20Sopenharmony_ci 214748, /* -80 */ 20588c2ecf20Sopenharmony_ci 67909, 20598c2ecf20Sopenharmony_ci 21475, /* -100 */ 20608c2ecf20Sopenharmony_ci 6791, 20618c2ecf20Sopenharmony_ci 2147, 20628c2ecf20Sopenharmony_ci 679, 20638c2ecf20Sopenharmony_ci 214, 20648c2ecf20Sopenharmony_ci 68, 20658c2ecf20Sopenharmony_ci 21, 20668c2ecf20Sopenharmony_ci 7, 20678c2ecf20Sopenharmony_ci 2 20688c2ecf20Sopenharmony_ci}; 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_cistatic int snd_asihpi_meter_get(struct snd_kcontrol *kcontrol, 20718c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 20728c2ecf20Sopenharmony_ci{ 20738c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 20748c2ecf20Sopenharmony_ci short an_gain_mB[HPI_MAX_CHANNELS], i; 20758c2ecf20Sopenharmony_ci u16 err; 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci err = hpi_meter_get_peak(h_control, an_gain_mB); 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci for (i = 0; i < HPI_MAX_CHANNELS; i++) { 20808c2ecf20Sopenharmony_ci if (err) { 20818c2ecf20Sopenharmony_ci ucontrol->value.integer.value[i] = 0; 20828c2ecf20Sopenharmony_ci } else if (an_gain_mB[i] >= 0) { 20838c2ecf20Sopenharmony_ci ucontrol->value.integer.value[i] = 20848c2ecf20Sopenharmony_ci an_gain_mB[i] << 16; 20858c2ecf20Sopenharmony_ci } else { 20868c2ecf20Sopenharmony_ci /* -ve is log value in millibels < -60dB, 20878c2ecf20Sopenharmony_ci * convert to (roughly!) linear, 20888c2ecf20Sopenharmony_ci */ 20898c2ecf20Sopenharmony_ci ucontrol->value.integer.value[i] = 20908c2ecf20Sopenharmony_ci log2lin[an_gain_mB[i] / -1000]; 20918c2ecf20Sopenharmony_ci } 20928c2ecf20Sopenharmony_ci } 20938c2ecf20Sopenharmony_ci return 0; 20948c2ecf20Sopenharmony_ci} 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_cistatic int snd_asihpi_meter_add(struct snd_card_asihpi *asihpi, 20978c2ecf20Sopenharmony_ci struct hpi_control *hpi_ctl, int subidx) 20988c2ecf20Sopenharmony_ci{ 20998c2ecf20Sopenharmony_ci struct snd_card *card = asihpi->card; 21008c2ecf20Sopenharmony_ci struct snd_kcontrol_new snd_control; 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Meter"); 21038c2ecf20Sopenharmony_ci snd_control.access = 21048c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; 21058c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_meter_info; 21068c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_meter_get; 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci snd_control.index = subidx; 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 21118c2ecf20Sopenharmony_ci} 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci/*------------------------------------------------------------ 21148c2ecf20Sopenharmony_ci Multiplexer controls 21158c2ecf20Sopenharmony_ci ------------------------------------------------------------*/ 21168c2ecf20Sopenharmony_cistatic int snd_card_asihpi_mux_count_sources(struct snd_kcontrol *snd_control) 21178c2ecf20Sopenharmony_ci{ 21188c2ecf20Sopenharmony_ci u32 h_control = snd_control->private_value; 21198c2ecf20Sopenharmony_ci struct hpi_control hpi_ctl; 21208c2ecf20Sopenharmony_ci int s, err; 21218c2ecf20Sopenharmony_ci for (s = 0; s < 32; s++) { 21228c2ecf20Sopenharmony_ci err = hpi_multiplexer_query_source(h_control, s, 21238c2ecf20Sopenharmony_ci &hpi_ctl. 21248c2ecf20Sopenharmony_ci src_node_type, 21258c2ecf20Sopenharmony_ci &hpi_ctl. 21268c2ecf20Sopenharmony_ci src_node_index); 21278c2ecf20Sopenharmony_ci if (err) 21288c2ecf20Sopenharmony_ci break; 21298c2ecf20Sopenharmony_ci } 21308c2ecf20Sopenharmony_ci return s; 21318c2ecf20Sopenharmony_ci} 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_cistatic int snd_asihpi_mux_info(struct snd_kcontrol *kcontrol, 21348c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 21358c2ecf20Sopenharmony_ci{ 21368c2ecf20Sopenharmony_ci u16 src_node_type, src_node_index; 21378c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 21408c2ecf20Sopenharmony_ci uinfo->count = 1; 21418c2ecf20Sopenharmony_ci uinfo->value.enumerated.items = 21428c2ecf20Sopenharmony_ci snd_card_asihpi_mux_count_sources(kcontrol); 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 21458c2ecf20Sopenharmony_ci uinfo->value.enumerated.item = 21468c2ecf20Sopenharmony_ci uinfo->value.enumerated.items - 1; 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci hpi_multiplexer_query_source(h_control, 21498c2ecf20Sopenharmony_ci uinfo->value.enumerated.item, 21508c2ecf20Sopenharmony_ci &src_node_type, &src_node_index); 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci sprintf(uinfo->value.enumerated.name, "%s %d", 21538c2ecf20Sopenharmony_ci asihpi_src_names[src_node_type - HPI_SOURCENODE_NONE], 21548c2ecf20Sopenharmony_ci src_node_index); 21558c2ecf20Sopenharmony_ci return 0; 21568c2ecf20Sopenharmony_ci} 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_cistatic int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol, 21598c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 21608c2ecf20Sopenharmony_ci{ 21618c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 21628c2ecf20Sopenharmony_ci u16 source_type, source_index; 21638c2ecf20Sopenharmony_ci u16 src_node_type, src_node_index; 21648c2ecf20Sopenharmony_ci int s; 21658c2ecf20Sopenharmony_ci 21668c2ecf20Sopenharmony_ci hpi_handle_error(hpi_multiplexer_get_source(h_control, 21678c2ecf20Sopenharmony_ci &source_type, &source_index)); 21688c2ecf20Sopenharmony_ci /* Should cache this search result! */ 21698c2ecf20Sopenharmony_ci for (s = 0; s < 256; s++) { 21708c2ecf20Sopenharmony_ci if (hpi_multiplexer_query_source(h_control, s, 21718c2ecf20Sopenharmony_ci &src_node_type, &src_node_index)) 21728c2ecf20Sopenharmony_ci break; 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci if ((source_type == src_node_type) 21758c2ecf20Sopenharmony_ci && (source_index == src_node_index)) { 21768c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = s; 21778c2ecf20Sopenharmony_ci return 0; 21788c2ecf20Sopenharmony_ci } 21798c2ecf20Sopenharmony_ci } 21808c2ecf20Sopenharmony_ci snd_printd(KERN_WARNING 21818c2ecf20Sopenharmony_ci "Control %x failed to match mux source %hu %hu\n", 21828c2ecf20Sopenharmony_ci h_control, source_type, source_index); 21838c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = 0; 21848c2ecf20Sopenharmony_ci return 0; 21858c2ecf20Sopenharmony_ci} 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_cistatic int snd_asihpi_mux_put(struct snd_kcontrol *kcontrol, 21888c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 21898c2ecf20Sopenharmony_ci{ 21908c2ecf20Sopenharmony_ci int change; 21918c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 21928c2ecf20Sopenharmony_ci u16 source_type, source_index; 21938c2ecf20Sopenharmony_ci u16 e; 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci change = 1; 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci e = hpi_multiplexer_query_source(h_control, 21988c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0], 21998c2ecf20Sopenharmony_ci &source_type, &source_index); 22008c2ecf20Sopenharmony_ci if (!e) 22018c2ecf20Sopenharmony_ci hpi_handle_error( 22028c2ecf20Sopenharmony_ci hpi_multiplexer_set_source(h_control, 22038c2ecf20Sopenharmony_ci source_type, source_index)); 22048c2ecf20Sopenharmony_ci return change; 22058c2ecf20Sopenharmony_ci} 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_cistatic int snd_asihpi_mux_add(struct snd_card_asihpi *asihpi, 22098c2ecf20Sopenharmony_ci struct hpi_control *hpi_ctl) 22108c2ecf20Sopenharmony_ci{ 22118c2ecf20Sopenharmony_ci struct snd_card *card = asihpi->card; 22128c2ecf20Sopenharmony_ci struct snd_kcontrol_new snd_control; 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Route"); 22158c2ecf20Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 22168c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_mux_info; 22178c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_mux_get; 22188c2ecf20Sopenharmony_ci snd_control.put = snd_asihpi_mux_put; 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci} 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci/*------------------------------------------------------------ 22258c2ecf20Sopenharmony_ci Channel mode controls 22268c2ecf20Sopenharmony_ci ------------------------------------------------------------*/ 22278c2ecf20Sopenharmony_cistatic int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol, 22288c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 22298c2ecf20Sopenharmony_ci{ 22308c2ecf20Sopenharmony_ci static const char * const mode_names[HPI_CHANNEL_MODE_LAST + 1] = { 22318c2ecf20Sopenharmony_ci "invalid", 22328c2ecf20Sopenharmony_ci "Normal", "Swap", 22338c2ecf20Sopenharmony_ci "From Left", "From Right", 22348c2ecf20Sopenharmony_ci "To Left", "To Right" 22358c2ecf20Sopenharmony_ci }; 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 22388c2ecf20Sopenharmony_ci u16 mode; 22398c2ecf20Sopenharmony_ci int i; 22408c2ecf20Sopenharmony_ci const char *mapped_names[6]; 22418c2ecf20Sopenharmony_ci int valid_modes = 0; 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci /* HPI channel mode values can be from 1 to 6 22448c2ecf20Sopenharmony_ci Some adapters only support a contiguous subset 22458c2ecf20Sopenharmony_ci */ 22468c2ecf20Sopenharmony_ci for (i = 0; i < HPI_CHANNEL_MODE_LAST; i++) 22478c2ecf20Sopenharmony_ci if (!hpi_channel_mode_query_mode( 22488c2ecf20Sopenharmony_ci h_control, i, &mode)) { 22498c2ecf20Sopenharmony_ci mapped_names[valid_modes] = mode_names[mode]; 22508c2ecf20Sopenharmony_ci valid_modes++; 22518c2ecf20Sopenharmony_ci } 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci if (!valid_modes) 22548c2ecf20Sopenharmony_ci return -EINVAL; 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, valid_modes, mapped_names); 22578c2ecf20Sopenharmony_ci} 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_cistatic int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol, 22608c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 22618c2ecf20Sopenharmony_ci{ 22628c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 22638c2ecf20Sopenharmony_ci u16 mode; 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci if (hpi_channel_mode_get(h_control, &mode)) 22668c2ecf20Sopenharmony_ci mode = 1; 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = mode - 1; 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci return 0; 22718c2ecf20Sopenharmony_ci} 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_cistatic int snd_asihpi_cmode_put(struct snd_kcontrol *kcontrol, 22748c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 22758c2ecf20Sopenharmony_ci{ 22768c2ecf20Sopenharmony_ci int change; 22778c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci change = 1; 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_ci hpi_handle_error(hpi_channel_mode_set(h_control, 22828c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] + 1)); 22838c2ecf20Sopenharmony_ci return change; 22848c2ecf20Sopenharmony_ci} 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_cistatic int snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi, 22888c2ecf20Sopenharmony_ci struct hpi_control *hpi_ctl) 22898c2ecf20Sopenharmony_ci{ 22908c2ecf20Sopenharmony_ci struct snd_card *card = asihpi->card; 22918c2ecf20Sopenharmony_ci struct snd_kcontrol_new snd_control; 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Mode"); 22948c2ecf20Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 22958c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_cmode_info; 22968c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_cmode_get; 22978c2ecf20Sopenharmony_ci snd_control.put = snd_asihpi_cmode_put; 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 23008c2ecf20Sopenharmony_ci} 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci/*------------------------------------------------------------ 23038c2ecf20Sopenharmony_ci Sampleclock source controls 23048c2ecf20Sopenharmony_ci ------------------------------------------------------------*/ 23058c2ecf20Sopenharmony_cistatic const char * const sampleclock_sources[] = { 23068c2ecf20Sopenharmony_ci "N/A", "Local PLL", "Digital Sync", "Word External", "Word Header", 23078c2ecf20Sopenharmony_ci "SMPTE", "Digital1", "Auto", "Network", "Invalid", 23088c2ecf20Sopenharmony_ci "Prev Module", "BLU-Link", 23098c2ecf20Sopenharmony_ci "Digital2", "Digital3", "Digital4", "Digital5", 23108c2ecf20Sopenharmony_ci "Digital6", "Digital7", "Digital8"}; 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_ci /* Number of strings must match expected enumerated values */ 23138c2ecf20Sopenharmony_ci compile_time_assert( 23148c2ecf20Sopenharmony_ci (ARRAY_SIZE(sampleclock_sources) == MAX_CLOCKSOURCES), 23158c2ecf20Sopenharmony_ci assert_sampleclock_sources_size); 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_cistatic int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol, 23188c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 23198c2ecf20Sopenharmony_ci{ 23208c2ecf20Sopenharmony_ci struct snd_card_asihpi *asihpi = 23218c2ecf20Sopenharmony_ci (struct snd_card_asihpi *)(kcontrol->private_data); 23228c2ecf20Sopenharmony_ci struct clk_cache *clkcache = &asihpi->cc; 23238c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 23248c2ecf20Sopenharmony_ci uinfo->count = 1; 23258c2ecf20Sopenharmony_ci uinfo->value.enumerated.items = clkcache->count; 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 23288c2ecf20Sopenharmony_ci uinfo->value.enumerated.item = 23298c2ecf20Sopenharmony_ci uinfo->value.enumerated.items - 1; 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci strcpy(uinfo->value.enumerated.name, 23328c2ecf20Sopenharmony_ci clkcache->s[uinfo->value.enumerated.item].name); 23338c2ecf20Sopenharmony_ci return 0; 23348c2ecf20Sopenharmony_ci} 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_cistatic int snd_asihpi_clksrc_get(struct snd_kcontrol *kcontrol, 23378c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 23388c2ecf20Sopenharmony_ci{ 23398c2ecf20Sopenharmony_ci struct snd_card_asihpi *asihpi = 23408c2ecf20Sopenharmony_ci (struct snd_card_asihpi *)(kcontrol->private_data); 23418c2ecf20Sopenharmony_ci struct clk_cache *clkcache = &asihpi->cc; 23428c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 23438c2ecf20Sopenharmony_ci u16 source, srcindex = 0; 23448c2ecf20Sopenharmony_ci int i; 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = 0; 23478c2ecf20Sopenharmony_ci if (hpi_sample_clock_get_source(h_control, &source)) 23488c2ecf20Sopenharmony_ci source = 0; 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_ci if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT) 23518c2ecf20Sopenharmony_ci if (hpi_sample_clock_get_source_index(h_control, &srcindex)) 23528c2ecf20Sopenharmony_ci srcindex = 0; 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci for (i = 0; i < clkcache->count; i++) 23558c2ecf20Sopenharmony_ci if ((clkcache->s[i].source == source) && 23568c2ecf20Sopenharmony_ci (clkcache->s[i].index == srcindex)) 23578c2ecf20Sopenharmony_ci break; 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = i; 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci return 0; 23628c2ecf20Sopenharmony_ci} 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_cistatic int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol, 23658c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 23668c2ecf20Sopenharmony_ci{ 23678c2ecf20Sopenharmony_ci struct snd_card_asihpi *asihpi = 23688c2ecf20Sopenharmony_ci (struct snd_card_asihpi *)(kcontrol->private_data); 23698c2ecf20Sopenharmony_ci struct clk_cache *clkcache = &asihpi->cc; 23708c2ecf20Sopenharmony_ci unsigned int item; 23718c2ecf20Sopenharmony_ci int change; 23728c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 23738c2ecf20Sopenharmony_ci 23748c2ecf20Sopenharmony_ci change = 1; 23758c2ecf20Sopenharmony_ci item = ucontrol->value.enumerated.item[0]; 23768c2ecf20Sopenharmony_ci if (item >= clkcache->count) 23778c2ecf20Sopenharmony_ci item = clkcache->count-1; 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci hpi_handle_error(hpi_sample_clock_set_source( 23808c2ecf20Sopenharmony_ci h_control, clkcache->s[item].source)); 23818c2ecf20Sopenharmony_ci 23828c2ecf20Sopenharmony_ci if (clkcache->s[item].source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT) 23838c2ecf20Sopenharmony_ci hpi_handle_error(hpi_sample_clock_set_source_index( 23848c2ecf20Sopenharmony_ci h_control, clkcache->s[item].index)); 23858c2ecf20Sopenharmony_ci return change; 23868c2ecf20Sopenharmony_ci} 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_ci/*------------------------------------------------------------ 23898c2ecf20Sopenharmony_ci Clkrate controls 23908c2ecf20Sopenharmony_ci ------------------------------------------------------------*/ 23918c2ecf20Sopenharmony_ci/* Need to change this to enumerated control with list of rates */ 23928c2ecf20Sopenharmony_cistatic int snd_asihpi_clklocal_info(struct snd_kcontrol *kcontrol, 23938c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 23948c2ecf20Sopenharmony_ci{ 23958c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 23968c2ecf20Sopenharmony_ci uinfo->count = 1; 23978c2ecf20Sopenharmony_ci uinfo->value.integer.min = 8000; 23988c2ecf20Sopenharmony_ci uinfo->value.integer.max = 192000; 23998c2ecf20Sopenharmony_ci uinfo->value.integer.step = 100; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci return 0; 24028c2ecf20Sopenharmony_ci} 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_cistatic int snd_asihpi_clklocal_get(struct snd_kcontrol *kcontrol, 24058c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 24068c2ecf20Sopenharmony_ci{ 24078c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 24088c2ecf20Sopenharmony_ci u32 rate; 24098c2ecf20Sopenharmony_ci u16 e; 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_ci e = hpi_sample_clock_get_local_rate(h_control, &rate); 24128c2ecf20Sopenharmony_ci if (!e) 24138c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = rate; 24148c2ecf20Sopenharmony_ci else 24158c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 0; 24168c2ecf20Sopenharmony_ci return 0; 24178c2ecf20Sopenharmony_ci} 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_cistatic int snd_asihpi_clklocal_put(struct snd_kcontrol *kcontrol, 24208c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 24218c2ecf20Sopenharmony_ci{ 24228c2ecf20Sopenharmony_ci int change; 24238c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci /* change = asihpi->mixer_clkrate[addr][0] != left || 24268c2ecf20Sopenharmony_ci asihpi->mixer_clkrate[addr][1] != right; 24278c2ecf20Sopenharmony_ci */ 24288c2ecf20Sopenharmony_ci change = 1; 24298c2ecf20Sopenharmony_ci hpi_handle_error(hpi_sample_clock_set_local_rate(h_control, 24308c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0])); 24318c2ecf20Sopenharmony_ci return change; 24328c2ecf20Sopenharmony_ci} 24338c2ecf20Sopenharmony_ci 24348c2ecf20Sopenharmony_cistatic int snd_asihpi_clkrate_info(struct snd_kcontrol *kcontrol, 24358c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 24368c2ecf20Sopenharmony_ci{ 24378c2ecf20Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 24388c2ecf20Sopenharmony_ci uinfo->count = 1; 24398c2ecf20Sopenharmony_ci uinfo->value.integer.min = 8000; 24408c2ecf20Sopenharmony_ci uinfo->value.integer.max = 192000; 24418c2ecf20Sopenharmony_ci uinfo->value.integer.step = 100; 24428c2ecf20Sopenharmony_ci 24438c2ecf20Sopenharmony_ci return 0; 24448c2ecf20Sopenharmony_ci} 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_cistatic int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol, 24478c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 24488c2ecf20Sopenharmony_ci{ 24498c2ecf20Sopenharmony_ci u32 h_control = kcontrol->private_value; 24508c2ecf20Sopenharmony_ci u32 rate; 24518c2ecf20Sopenharmony_ci u16 e; 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_ci e = hpi_sample_clock_get_sample_rate(h_control, &rate); 24548c2ecf20Sopenharmony_ci if (!e) 24558c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = rate; 24568c2ecf20Sopenharmony_ci else 24578c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = 0; 24588c2ecf20Sopenharmony_ci return 0; 24598c2ecf20Sopenharmony_ci} 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_cistatic int snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi, 24628c2ecf20Sopenharmony_ci struct hpi_control *hpi_ctl) 24638c2ecf20Sopenharmony_ci{ 24648c2ecf20Sopenharmony_ci struct snd_card *card; 24658c2ecf20Sopenharmony_ci struct snd_kcontrol_new snd_control; 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_ci struct clk_cache *clkcache; 24688c2ecf20Sopenharmony_ci u32 hSC = hpi_ctl->h_control; 24698c2ecf20Sopenharmony_ci int has_aes_in = 0; 24708c2ecf20Sopenharmony_ci int i, j; 24718c2ecf20Sopenharmony_ci u16 source; 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_ci if (snd_BUG_ON(!asihpi)) 24748c2ecf20Sopenharmony_ci return -EINVAL; 24758c2ecf20Sopenharmony_ci card = asihpi->card; 24768c2ecf20Sopenharmony_ci clkcache = &asihpi->cc; 24778c2ecf20Sopenharmony_ci snd_control.private_value = hpi_ctl->h_control; 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci clkcache->has_local = 0; 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci for (i = 0; i <= HPI_SAMPLECLOCK_SOURCE_LAST; i++) { 24828c2ecf20Sopenharmony_ci if (hpi_sample_clock_query_source(hSC, 24838c2ecf20Sopenharmony_ci i, &source)) 24848c2ecf20Sopenharmony_ci break; 24858c2ecf20Sopenharmony_ci clkcache->s[i].source = source; 24868c2ecf20Sopenharmony_ci clkcache->s[i].index = 0; 24878c2ecf20Sopenharmony_ci clkcache->s[i].name = sampleclock_sources[source]; 24888c2ecf20Sopenharmony_ci if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT) 24898c2ecf20Sopenharmony_ci has_aes_in = 1; 24908c2ecf20Sopenharmony_ci if (source == HPI_SAMPLECLOCK_SOURCE_LOCAL) 24918c2ecf20Sopenharmony_ci clkcache->has_local = 1; 24928c2ecf20Sopenharmony_ci } 24938c2ecf20Sopenharmony_ci if (has_aes_in) 24948c2ecf20Sopenharmony_ci /* already will have picked up index 0 above */ 24958c2ecf20Sopenharmony_ci for (j = 1; j < 8; j++) { 24968c2ecf20Sopenharmony_ci if (hpi_sample_clock_query_source_index(hSC, 24978c2ecf20Sopenharmony_ci j, HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT, 24988c2ecf20Sopenharmony_ci &source)) 24998c2ecf20Sopenharmony_ci break; 25008c2ecf20Sopenharmony_ci clkcache->s[i].source = 25018c2ecf20Sopenharmony_ci HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT; 25028c2ecf20Sopenharmony_ci clkcache->s[i].index = j; 25038c2ecf20Sopenharmony_ci clkcache->s[i].name = sampleclock_sources[ 25048c2ecf20Sopenharmony_ci j+HPI_SAMPLECLOCK_SOURCE_LAST]; 25058c2ecf20Sopenharmony_ci i++; 25068c2ecf20Sopenharmony_ci } 25078c2ecf20Sopenharmony_ci clkcache->count = i; 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Source"); 25108c2ecf20Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ; 25118c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_clksrc_info; 25128c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_clksrc_get; 25138c2ecf20Sopenharmony_ci snd_control.put = snd_asihpi_clksrc_put; 25148c2ecf20Sopenharmony_ci if (ctl_add(card, &snd_control, asihpi) < 0) 25158c2ecf20Sopenharmony_ci return -EINVAL; 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci 25188c2ecf20Sopenharmony_ci if (clkcache->has_local) { 25198c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Localrate"); 25208c2ecf20Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ; 25218c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_clklocal_info; 25228c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_clklocal_get; 25238c2ecf20Sopenharmony_ci snd_control.put = snd_asihpi_clklocal_put; 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ci 25268c2ecf20Sopenharmony_ci if (ctl_add(card, &snd_control, asihpi) < 0) 25278c2ecf20Sopenharmony_ci return -EINVAL; 25288c2ecf20Sopenharmony_ci } 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Rate"); 25318c2ecf20Sopenharmony_ci snd_control.access = 25328c2ecf20Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; 25338c2ecf20Sopenharmony_ci snd_control.info = snd_asihpi_clkrate_info; 25348c2ecf20Sopenharmony_ci snd_control.get = snd_asihpi_clkrate_get; 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 25378c2ecf20Sopenharmony_ci} 25388c2ecf20Sopenharmony_ci/*------------------------------------------------------------ 25398c2ecf20Sopenharmony_ci Mixer 25408c2ecf20Sopenharmony_ci ------------------------------------------------------------*/ 25418c2ecf20Sopenharmony_ci 25428c2ecf20Sopenharmony_cistatic int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) 25438c2ecf20Sopenharmony_ci{ 25448c2ecf20Sopenharmony_ci struct snd_card *card; 25458c2ecf20Sopenharmony_ci unsigned int idx = 0; 25468c2ecf20Sopenharmony_ci unsigned int subindex = 0; 25478c2ecf20Sopenharmony_ci int err; 25488c2ecf20Sopenharmony_ci struct hpi_control hpi_ctl, prev_ctl; 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci if (snd_BUG_ON(!asihpi)) 25518c2ecf20Sopenharmony_ci return -EINVAL; 25528c2ecf20Sopenharmony_ci card = asihpi->card; 25538c2ecf20Sopenharmony_ci strcpy(card->mixername, "Asihpi Mixer"); 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci err = 25568c2ecf20Sopenharmony_ci hpi_mixer_open(asihpi->hpi->adapter->index, 25578c2ecf20Sopenharmony_ci &asihpi->h_mixer); 25588c2ecf20Sopenharmony_ci hpi_handle_error(err); 25598c2ecf20Sopenharmony_ci if (err) 25608c2ecf20Sopenharmony_ci return -err; 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci memset(&prev_ctl, 0, sizeof(prev_ctl)); 25638c2ecf20Sopenharmony_ci prev_ctl.control_type = -1; 25648c2ecf20Sopenharmony_ci 25658c2ecf20Sopenharmony_ci for (idx = 0; idx < 2000; idx++) { 25668c2ecf20Sopenharmony_ci err = hpi_mixer_get_control_by_index( 25678c2ecf20Sopenharmony_ci asihpi->h_mixer, 25688c2ecf20Sopenharmony_ci idx, 25698c2ecf20Sopenharmony_ci &hpi_ctl.src_node_type, 25708c2ecf20Sopenharmony_ci &hpi_ctl.src_node_index, 25718c2ecf20Sopenharmony_ci &hpi_ctl.dst_node_type, 25728c2ecf20Sopenharmony_ci &hpi_ctl.dst_node_index, 25738c2ecf20Sopenharmony_ci &hpi_ctl.control_type, 25748c2ecf20Sopenharmony_ci &hpi_ctl.h_control); 25758c2ecf20Sopenharmony_ci if (err) { 25768c2ecf20Sopenharmony_ci if (err == HPI_ERROR_CONTROL_DISABLED) { 25778c2ecf20Sopenharmony_ci if (mixer_dump) 25788c2ecf20Sopenharmony_ci dev_info(&asihpi->pci->dev, 25798c2ecf20Sopenharmony_ci "Disabled HPI Control(%d)\n", 25808c2ecf20Sopenharmony_ci idx); 25818c2ecf20Sopenharmony_ci continue; 25828c2ecf20Sopenharmony_ci } else 25838c2ecf20Sopenharmony_ci break; 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_ci } 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ci hpi_ctl.src_node_type -= HPI_SOURCENODE_NONE; 25888c2ecf20Sopenharmony_ci hpi_ctl.dst_node_type -= HPI_DESTNODE_NONE; 25898c2ecf20Sopenharmony_ci 25908c2ecf20Sopenharmony_ci /* ASI50xx in SSX mode has multiple meters on the same node. 25918c2ecf20Sopenharmony_ci Use subindex to create distinct ALSA controls 25928c2ecf20Sopenharmony_ci for any duplicated controls. 25938c2ecf20Sopenharmony_ci */ 25948c2ecf20Sopenharmony_ci if ((hpi_ctl.control_type == prev_ctl.control_type) && 25958c2ecf20Sopenharmony_ci (hpi_ctl.src_node_type == prev_ctl.src_node_type) && 25968c2ecf20Sopenharmony_ci (hpi_ctl.src_node_index == prev_ctl.src_node_index) && 25978c2ecf20Sopenharmony_ci (hpi_ctl.dst_node_type == prev_ctl.dst_node_type) && 25988c2ecf20Sopenharmony_ci (hpi_ctl.dst_node_index == prev_ctl.dst_node_index)) 25998c2ecf20Sopenharmony_ci subindex++; 26008c2ecf20Sopenharmony_ci else 26018c2ecf20Sopenharmony_ci subindex = 0; 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci prev_ctl = hpi_ctl; 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci switch (hpi_ctl.control_type) { 26068c2ecf20Sopenharmony_ci case HPI_CONTROL_VOLUME: 26078c2ecf20Sopenharmony_ci err = snd_asihpi_volume_add(asihpi, &hpi_ctl); 26088c2ecf20Sopenharmony_ci break; 26098c2ecf20Sopenharmony_ci case HPI_CONTROL_LEVEL: 26108c2ecf20Sopenharmony_ci err = snd_asihpi_level_add(asihpi, &hpi_ctl); 26118c2ecf20Sopenharmony_ci break; 26128c2ecf20Sopenharmony_ci case HPI_CONTROL_MULTIPLEXER: 26138c2ecf20Sopenharmony_ci err = snd_asihpi_mux_add(asihpi, &hpi_ctl); 26148c2ecf20Sopenharmony_ci break; 26158c2ecf20Sopenharmony_ci case HPI_CONTROL_CHANNEL_MODE: 26168c2ecf20Sopenharmony_ci err = snd_asihpi_cmode_add(asihpi, &hpi_ctl); 26178c2ecf20Sopenharmony_ci break; 26188c2ecf20Sopenharmony_ci case HPI_CONTROL_METER: 26198c2ecf20Sopenharmony_ci err = snd_asihpi_meter_add(asihpi, &hpi_ctl, subindex); 26208c2ecf20Sopenharmony_ci break; 26218c2ecf20Sopenharmony_ci case HPI_CONTROL_SAMPLECLOCK: 26228c2ecf20Sopenharmony_ci err = snd_asihpi_sampleclock_add( 26238c2ecf20Sopenharmony_ci asihpi, &hpi_ctl); 26248c2ecf20Sopenharmony_ci break; 26258c2ecf20Sopenharmony_ci case HPI_CONTROL_CONNECTION: /* ignore these */ 26268c2ecf20Sopenharmony_ci continue; 26278c2ecf20Sopenharmony_ci case HPI_CONTROL_TUNER: 26288c2ecf20Sopenharmony_ci err = snd_asihpi_tuner_add(asihpi, &hpi_ctl); 26298c2ecf20Sopenharmony_ci break; 26308c2ecf20Sopenharmony_ci case HPI_CONTROL_AESEBU_TRANSMITTER: 26318c2ecf20Sopenharmony_ci err = snd_asihpi_aesebu_tx_add(asihpi, &hpi_ctl); 26328c2ecf20Sopenharmony_ci break; 26338c2ecf20Sopenharmony_ci case HPI_CONTROL_AESEBU_RECEIVER: 26348c2ecf20Sopenharmony_ci err = snd_asihpi_aesebu_rx_add(asihpi, &hpi_ctl); 26358c2ecf20Sopenharmony_ci break; 26368c2ecf20Sopenharmony_ci case HPI_CONTROL_VOX: 26378c2ecf20Sopenharmony_ci case HPI_CONTROL_BITSTREAM: 26388c2ecf20Sopenharmony_ci case HPI_CONTROL_MICROPHONE: 26398c2ecf20Sopenharmony_ci case HPI_CONTROL_PARAMETRIC_EQ: 26408c2ecf20Sopenharmony_ci case HPI_CONTROL_COMPANDER: 26418c2ecf20Sopenharmony_ci default: 26428c2ecf20Sopenharmony_ci if (mixer_dump) 26438c2ecf20Sopenharmony_ci dev_info(&asihpi->pci->dev, 26448c2ecf20Sopenharmony_ci "Untranslated HPI Control (%d) %d %d %d %d %d\n", 26458c2ecf20Sopenharmony_ci idx, 26468c2ecf20Sopenharmony_ci hpi_ctl.control_type, 26478c2ecf20Sopenharmony_ci hpi_ctl.src_node_type, 26488c2ecf20Sopenharmony_ci hpi_ctl.src_node_index, 26498c2ecf20Sopenharmony_ci hpi_ctl.dst_node_type, 26508c2ecf20Sopenharmony_ci hpi_ctl.dst_node_index); 26518c2ecf20Sopenharmony_ci continue; 26528c2ecf20Sopenharmony_ci } 26538c2ecf20Sopenharmony_ci if (err < 0) 26548c2ecf20Sopenharmony_ci return err; 26558c2ecf20Sopenharmony_ci } 26568c2ecf20Sopenharmony_ci if (HPI_ERROR_INVALID_OBJ_INDEX != err) 26578c2ecf20Sopenharmony_ci hpi_handle_error(err); 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_ci dev_info(&asihpi->pci->dev, "%d mixer controls found\n", idx); 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ci return 0; 26628c2ecf20Sopenharmony_ci} 26638c2ecf20Sopenharmony_ci 26648c2ecf20Sopenharmony_ci/*------------------------------------------------------------ 26658c2ecf20Sopenharmony_ci /proc interface 26668c2ecf20Sopenharmony_ci ------------------------------------------------------------*/ 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_cistatic void 26698c2ecf20Sopenharmony_cisnd_asihpi_proc_read(struct snd_info_entry *entry, 26708c2ecf20Sopenharmony_ci struct snd_info_buffer *buffer) 26718c2ecf20Sopenharmony_ci{ 26728c2ecf20Sopenharmony_ci struct snd_card_asihpi *asihpi = entry->private_data; 26738c2ecf20Sopenharmony_ci u32 h_control; 26748c2ecf20Sopenharmony_ci u32 rate = 0; 26758c2ecf20Sopenharmony_ci u16 source = 0; 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci u16 num_outstreams; 26788c2ecf20Sopenharmony_ci u16 num_instreams; 26798c2ecf20Sopenharmony_ci u16 version; 26808c2ecf20Sopenharmony_ci u32 serial_number; 26818c2ecf20Sopenharmony_ci u16 type; 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_ci int err; 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci snd_iprintf(buffer, "ASIHPI driver proc file\n"); 26868c2ecf20Sopenharmony_ci 26878c2ecf20Sopenharmony_ci hpi_handle_error(hpi_adapter_get_info(asihpi->hpi->adapter->index, 26888c2ecf20Sopenharmony_ci &num_outstreams, &num_instreams, 26898c2ecf20Sopenharmony_ci &version, &serial_number, &type)); 26908c2ecf20Sopenharmony_ci 26918c2ecf20Sopenharmony_ci snd_iprintf(buffer, 26928c2ecf20Sopenharmony_ci "Adapter type ASI%4X\nHardware Index %d\n" 26938c2ecf20Sopenharmony_ci "%d outstreams\n%d instreams\n", 26948c2ecf20Sopenharmony_ci type, asihpi->hpi->adapter->index, 26958c2ecf20Sopenharmony_ci num_outstreams, num_instreams); 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_ci snd_iprintf(buffer, 26988c2ecf20Sopenharmony_ci "Serial#%d\nHardware version %c%d\nDSP code version %03d\n", 26998c2ecf20Sopenharmony_ci serial_number, ((version >> 3) & 0xf) + 'A', version & 0x7, 27008c2ecf20Sopenharmony_ci ((version >> 13) * 100) + ((version >> 7) & 0x3f)); 27018c2ecf20Sopenharmony_ci 27028c2ecf20Sopenharmony_ci err = hpi_mixer_get_control(asihpi->h_mixer, 27038c2ecf20Sopenharmony_ci HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, 27048c2ecf20Sopenharmony_ci HPI_CONTROL_SAMPLECLOCK, &h_control); 27058c2ecf20Sopenharmony_ci 27068c2ecf20Sopenharmony_ci if (!err) { 27078c2ecf20Sopenharmony_ci err = hpi_sample_clock_get_sample_rate(h_control, &rate); 27088c2ecf20Sopenharmony_ci err += hpi_sample_clock_get_source(h_control, &source); 27098c2ecf20Sopenharmony_ci 27108c2ecf20Sopenharmony_ci if (!err) 27118c2ecf20Sopenharmony_ci snd_iprintf(buffer, "Sample Clock %dHz, source %s\n", 27128c2ecf20Sopenharmony_ci rate, sampleclock_sources[source]); 27138c2ecf20Sopenharmony_ci } 27148c2ecf20Sopenharmony_ci} 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_cistatic void snd_asihpi_proc_init(struct snd_card_asihpi *asihpi) 27178c2ecf20Sopenharmony_ci{ 27188c2ecf20Sopenharmony_ci snd_card_ro_proc_new(asihpi->card, "info", asihpi, 27198c2ecf20Sopenharmony_ci snd_asihpi_proc_read); 27208c2ecf20Sopenharmony_ci} 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci/*------------------------------------------------------------ 27238c2ecf20Sopenharmony_ci HWDEP 27248c2ecf20Sopenharmony_ci ------------------------------------------------------------*/ 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_cistatic int snd_asihpi_hpi_open(struct snd_hwdep *hw, struct file *file) 27278c2ecf20Sopenharmony_ci{ 27288c2ecf20Sopenharmony_ci if (enable_hpi_hwdep) 27298c2ecf20Sopenharmony_ci return 0; 27308c2ecf20Sopenharmony_ci else 27318c2ecf20Sopenharmony_ci return -ENODEV; 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci} 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_cistatic int snd_asihpi_hpi_release(struct snd_hwdep *hw, struct file *file) 27368c2ecf20Sopenharmony_ci{ 27378c2ecf20Sopenharmony_ci if (enable_hpi_hwdep) 27388c2ecf20Sopenharmony_ci return asihpi_hpi_release(file); 27398c2ecf20Sopenharmony_ci else 27408c2ecf20Sopenharmony_ci return -ENODEV; 27418c2ecf20Sopenharmony_ci} 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_cistatic int snd_asihpi_hpi_ioctl(struct snd_hwdep *hw, struct file *file, 27448c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 27458c2ecf20Sopenharmony_ci{ 27468c2ecf20Sopenharmony_ci if (enable_hpi_hwdep) 27478c2ecf20Sopenharmony_ci return asihpi_hpi_ioctl(file, cmd, arg); 27488c2ecf20Sopenharmony_ci else 27498c2ecf20Sopenharmony_ci return -ENODEV; 27508c2ecf20Sopenharmony_ci} 27518c2ecf20Sopenharmony_ci 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci/* results in /dev/snd/hwC#D0 file for each card with index # 27548c2ecf20Sopenharmony_ci also /proc/asound/hwdep will contain '#-00: asihpi (HPI) for each card' 27558c2ecf20Sopenharmony_ci*/ 27568c2ecf20Sopenharmony_cistatic int snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, int device) 27578c2ecf20Sopenharmony_ci{ 27588c2ecf20Sopenharmony_ci struct snd_hwdep *hw; 27598c2ecf20Sopenharmony_ci int err; 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_ci err = snd_hwdep_new(asihpi->card, "HPI", device, &hw); 27628c2ecf20Sopenharmony_ci if (err < 0) 27638c2ecf20Sopenharmony_ci return err; 27648c2ecf20Sopenharmony_ci strcpy(hw->name, "asihpi (HPI)"); 27658c2ecf20Sopenharmony_ci hw->iface = SNDRV_HWDEP_IFACE_LAST; 27668c2ecf20Sopenharmony_ci hw->ops.open = snd_asihpi_hpi_open; 27678c2ecf20Sopenharmony_ci hw->ops.ioctl = snd_asihpi_hpi_ioctl; 27688c2ecf20Sopenharmony_ci hw->ops.release = snd_asihpi_hpi_release; 27698c2ecf20Sopenharmony_ci hw->private_data = asihpi; 27708c2ecf20Sopenharmony_ci return 0; 27718c2ecf20Sopenharmony_ci} 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci/*------------------------------------------------------------ 27748c2ecf20Sopenharmony_ci CARD 27758c2ecf20Sopenharmony_ci ------------------------------------------------------------*/ 27768c2ecf20Sopenharmony_cistatic int snd_asihpi_probe(struct pci_dev *pci_dev, 27778c2ecf20Sopenharmony_ci const struct pci_device_id *pci_id) 27788c2ecf20Sopenharmony_ci{ 27798c2ecf20Sopenharmony_ci int err; 27808c2ecf20Sopenharmony_ci struct hpi_adapter *hpi; 27818c2ecf20Sopenharmony_ci struct snd_card *card; 27828c2ecf20Sopenharmony_ci struct snd_card_asihpi *asihpi; 27838c2ecf20Sopenharmony_ci 27848c2ecf20Sopenharmony_ci u32 h_control; 27858c2ecf20Sopenharmony_ci u32 h_stream; 27868c2ecf20Sopenharmony_ci u32 adapter_index; 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci static int dev; 27898c2ecf20Sopenharmony_ci if (dev >= SNDRV_CARDS) 27908c2ecf20Sopenharmony_ci return -ENODEV; 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_ci /* Should this be enable[hpi->index] ? */ 27938c2ecf20Sopenharmony_ci if (!enable[dev]) { 27948c2ecf20Sopenharmony_ci dev++; 27958c2ecf20Sopenharmony_ci return -ENOENT; 27968c2ecf20Sopenharmony_ci } 27978c2ecf20Sopenharmony_ci 27988c2ecf20Sopenharmony_ci /* Initialise low-level HPI driver */ 27998c2ecf20Sopenharmony_ci err = asihpi_adapter_probe(pci_dev, pci_id); 28008c2ecf20Sopenharmony_ci if (err < 0) 28018c2ecf20Sopenharmony_ci return err; 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci hpi = pci_get_drvdata(pci_dev); 28048c2ecf20Sopenharmony_ci adapter_index = hpi->adapter->index; 28058c2ecf20Sopenharmony_ci /* first try to give the card the same index as its hardware index */ 28068c2ecf20Sopenharmony_ci err = snd_card_new(&pci_dev->dev, adapter_index, id[adapter_index], 28078c2ecf20Sopenharmony_ci THIS_MODULE, sizeof(struct snd_card_asihpi), &card); 28088c2ecf20Sopenharmony_ci if (err < 0) { 28098c2ecf20Sopenharmony_ci /* if that fails, try the default index==next available */ 28108c2ecf20Sopenharmony_ci err = snd_card_new(&pci_dev->dev, index[dev], id[dev], 28118c2ecf20Sopenharmony_ci THIS_MODULE, sizeof(struct snd_card_asihpi), 28128c2ecf20Sopenharmony_ci &card); 28138c2ecf20Sopenharmony_ci if (err < 0) 28148c2ecf20Sopenharmony_ci return err; 28158c2ecf20Sopenharmony_ci dev_warn(&pci_dev->dev, "Adapter index %d->ALSA index %d\n", 28168c2ecf20Sopenharmony_ci adapter_index, card->number); 28178c2ecf20Sopenharmony_ci } 28188c2ecf20Sopenharmony_ci 28198c2ecf20Sopenharmony_ci asihpi = card->private_data; 28208c2ecf20Sopenharmony_ci asihpi->card = card; 28218c2ecf20Sopenharmony_ci asihpi->pci = pci_dev; 28228c2ecf20Sopenharmony_ci asihpi->hpi = hpi; 28238c2ecf20Sopenharmony_ci hpi->snd_card = card; 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_ci err = hpi_adapter_get_property(adapter_index, 28268c2ecf20Sopenharmony_ci HPI_ADAPTER_PROPERTY_CAPS1, 28278c2ecf20Sopenharmony_ci NULL, &asihpi->support_grouping); 28288c2ecf20Sopenharmony_ci if (err) 28298c2ecf20Sopenharmony_ci asihpi->support_grouping = 0; 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci err = hpi_adapter_get_property(adapter_index, 28328c2ecf20Sopenharmony_ci HPI_ADAPTER_PROPERTY_CAPS2, 28338c2ecf20Sopenharmony_ci &asihpi->support_mrx, NULL); 28348c2ecf20Sopenharmony_ci if (err) 28358c2ecf20Sopenharmony_ci asihpi->support_mrx = 0; 28368c2ecf20Sopenharmony_ci 28378c2ecf20Sopenharmony_ci err = hpi_adapter_get_property(adapter_index, 28388c2ecf20Sopenharmony_ci HPI_ADAPTER_PROPERTY_INTERVAL, 28398c2ecf20Sopenharmony_ci NULL, &asihpi->update_interval_frames); 28408c2ecf20Sopenharmony_ci if (err) 28418c2ecf20Sopenharmony_ci asihpi->update_interval_frames = 512; 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci if (hpi->interrupt_mode) { 28448c2ecf20Sopenharmony_ci asihpi->pcm_start = snd_card_asihpi_pcm_int_start; 28458c2ecf20Sopenharmony_ci asihpi->pcm_stop = snd_card_asihpi_pcm_int_stop; 28468c2ecf20Sopenharmony_ci hpi->interrupt_callback = snd_card_asihpi_isr; 28478c2ecf20Sopenharmony_ci } else { 28488c2ecf20Sopenharmony_ci asihpi->pcm_start = snd_card_asihpi_pcm_timer_start; 28498c2ecf20Sopenharmony_ci asihpi->pcm_stop = snd_card_asihpi_pcm_timer_stop; 28508c2ecf20Sopenharmony_ci } 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci hpi_handle_error(hpi_instream_open(adapter_index, 28538c2ecf20Sopenharmony_ci 0, &h_stream)); 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci err = hpi_instream_host_buffer_free(h_stream); 28568c2ecf20Sopenharmony_ci asihpi->can_dma = (!err); 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci hpi_handle_error(hpi_instream_close(h_stream)); 28598c2ecf20Sopenharmony_ci 28608c2ecf20Sopenharmony_ci if (!asihpi->can_dma) 28618c2ecf20Sopenharmony_ci asihpi->update_interval_frames *= 2; 28628c2ecf20Sopenharmony_ci 28638c2ecf20Sopenharmony_ci err = hpi_adapter_get_property(adapter_index, 28648c2ecf20Sopenharmony_ci HPI_ADAPTER_PROPERTY_CURCHANNELS, 28658c2ecf20Sopenharmony_ci &asihpi->in_max_chans, &asihpi->out_max_chans); 28668c2ecf20Sopenharmony_ci if (err) { 28678c2ecf20Sopenharmony_ci asihpi->in_max_chans = 2; 28688c2ecf20Sopenharmony_ci asihpi->out_max_chans = 2; 28698c2ecf20Sopenharmony_ci } 28708c2ecf20Sopenharmony_ci 28718c2ecf20Sopenharmony_ci if (asihpi->out_max_chans > 2) { /* assume LL mode */ 28728c2ecf20Sopenharmony_ci asihpi->out_min_chans = asihpi->out_max_chans; 28738c2ecf20Sopenharmony_ci asihpi->in_min_chans = asihpi->in_max_chans; 28748c2ecf20Sopenharmony_ci asihpi->support_grouping = 0; 28758c2ecf20Sopenharmony_ci } else { 28768c2ecf20Sopenharmony_ci asihpi->out_min_chans = 1; 28778c2ecf20Sopenharmony_ci asihpi->in_min_chans = 1; 28788c2ecf20Sopenharmony_ci } 28798c2ecf20Sopenharmony_ci 28808c2ecf20Sopenharmony_ci dev_info(&pci_dev->dev, "Has dma:%d, grouping:%d, mrx:%d, uif:%d\n", 28818c2ecf20Sopenharmony_ci asihpi->can_dma, 28828c2ecf20Sopenharmony_ci asihpi->support_grouping, 28838c2ecf20Sopenharmony_ci asihpi->support_mrx, 28848c2ecf20Sopenharmony_ci asihpi->update_interval_frames 28858c2ecf20Sopenharmony_ci ); 28868c2ecf20Sopenharmony_ci 28878c2ecf20Sopenharmony_ci err = snd_card_asihpi_pcm_new(asihpi, 0); 28888c2ecf20Sopenharmony_ci if (err < 0) { 28898c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, "pcm_new failed\n"); 28908c2ecf20Sopenharmony_ci goto __nodev; 28918c2ecf20Sopenharmony_ci } 28928c2ecf20Sopenharmony_ci err = snd_card_asihpi_mixer_new(asihpi); 28938c2ecf20Sopenharmony_ci if (err < 0) { 28948c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, "mixer_new failed\n"); 28958c2ecf20Sopenharmony_ci goto __nodev; 28968c2ecf20Sopenharmony_ci } 28978c2ecf20Sopenharmony_ci 28988c2ecf20Sopenharmony_ci err = hpi_mixer_get_control(asihpi->h_mixer, 28998c2ecf20Sopenharmony_ci HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, 29008c2ecf20Sopenharmony_ci HPI_CONTROL_SAMPLECLOCK, &h_control); 29018c2ecf20Sopenharmony_ci 29028c2ecf20Sopenharmony_ci if (!err) 29038c2ecf20Sopenharmony_ci err = hpi_sample_clock_set_local_rate( 29048c2ecf20Sopenharmony_ci h_control, adapter_fs); 29058c2ecf20Sopenharmony_ci 29068c2ecf20Sopenharmony_ci snd_asihpi_proc_init(asihpi); 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_ci /* always create, can be enabled or disabled dynamically 29098c2ecf20Sopenharmony_ci by enable_hwdep module param*/ 29108c2ecf20Sopenharmony_ci snd_asihpi_hpi_new(asihpi, 0); 29118c2ecf20Sopenharmony_ci 29128c2ecf20Sopenharmony_ci strcpy(card->driver, "ASIHPI"); 29138c2ecf20Sopenharmony_ci 29148c2ecf20Sopenharmony_ci sprintf(card->shortname, "AudioScience ASI%4X", 29158c2ecf20Sopenharmony_ci asihpi->hpi->adapter->type); 29168c2ecf20Sopenharmony_ci sprintf(card->longname, "%s %i", 29178c2ecf20Sopenharmony_ci card->shortname, adapter_index); 29188c2ecf20Sopenharmony_ci err = snd_card_register(card); 29198c2ecf20Sopenharmony_ci 29208c2ecf20Sopenharmony_ci if (!err) { 29218c2ecf20Sopenharmony_ci dev++; 29228c2ecf20Sopenharmony_ci return 0; 29238c2ecf20Sopenharmony_ci } 29248c2ecf20Sopenharmony_ci__nodev: 29258c2ecf20Sopenharmony_ci snd_card_free(card); 29268c2ecf20Sopenharmony_ci dev_err(&pci_dev->dev, "snd_asihpi_probe error %d\n", err); 29278c2ecf20Sopenharmony_ci return err; 29288c2ecf20Sopenharmony_ci 29298c2ecf20Sopenharmony_ci} 29308c2ecf20Sopenharmony_ci 29318c2ecf20Sopenharmony_cistatic void snd_asihpi_remove(struct pci_dev *pci_dev) 29328c2ecf20Sopenharmony_ci{ 29338c2ecf20Sopenharmony_ci struct hpi_adapter *hpi = pci_get_drvdata(pci_dev); 29348c2ecf20Sopenharmony_ci 29358c2ecf20Sopenharmony_ci /* Stop interrupts */ 29368c2ecf20Sopenharmony_ci if (hpi->interrupt_mode) { 29378c2ecf20Sopenharmony_ci hpi->interrupt_callback = NULL; 29388c2ecf20Sopenharmony_ci hpi_handle_error(hpi_adapter_set_property(hpi->adapter->index, 29398c2ecf20Sopenharmony_ci HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0)); 29408c2ecf20Sopenharmony_ci } 29418c2ecf20Sopenharmony_ci 29428c2ecf20Sopenharmony_ci snd_card_free(hpi->snd_card); 29438c2ecf20Sopenharmony_ci hpi->snd_card = NULL; 29448c2ecf20Sopenharmony_ci asihpi_adapter_remove(pci_dev); 29458c2ecf20Sopenharmony_ci} 29468c2ecf20Sopenharmony_ci 29478c2ecf20Sopenharmony_cistatic const struct pci_device_id asihpi_pci_tbl[] = { 29488c2ecf20Sopenharmony_ci {HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_DSP6205, 29498c2ecf20Sopenharmony_ci HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0, 29508c2ecf20Sopenharmony_ci (kernel_ulong_t)HPI_6205}, 29518c2ecf20Sopenharmony_ci {HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_PCI2040, 29528c2ecf20Sopenharmony_ci HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0, 29538c2ecf20Sopenharmony_ci (kernel_ulong_t)HPI_6000}, 29548c2ecf20Sopenharmony_ci {0,} 29558c2ecf20Sopenharmony_ci}; 29568c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, asihpi_pci_tbl); 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_cistatic struct pci_driver driver = { 29598c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 29608c2ecf20Sopenharmony_ci .id_table = asihpi_pci_tbl, 29618c2ecf20Sopenharmony_ci .probe = snd_asihpi_probe, 29628c2ecf20Sopenharmony_ci .remove = snd_asihpi_remove, 29638c2ecf20Sopenharmony_ci}; 29648c2ecf20Sopenharmony_ci 29658c2ecf20Sopenharmony_cistatic int __init snd_asihpi_init(void) 29668c2ecf20Sopenharmony_ci{ 29678c2ecf20Sopenharmony_ci asihpi_init(); 29688c2ecf20Sopenharmony_ci return pci_register_driver(&driver); 29698c2ecf20Sopenharmony_ci} 29708c2ecf20Sopenharmony_ci 29718c2ecf20Sopenharmony_cistatic void __exit snd_asihpi_exit(void) 29728c2ecf20Sopenharmony_ci{ 29738c2ecf20Sopenharmony_ci 29748c2ecf20Sopenharmony_ci pci_unregister_driver(&driver); 29758c2ecf20Sopenharmony_ci asihpi_exit(); 29768c2ecf20Sopenharmony_ci} 29778c2ecf20Sopenharmony_ci 29788c2ecf20Sopenharmony_cimodule_init(snd_asihpi_init) 29798c2ecf20Sopenharmony_cimodule_exit(snd_asihpi_exit) 29808c2ecf20Sopenharmony_ci 2981