162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Asihpi soundcard 462306a36Sopenharmony_ci * Copyright (c) by AudioScience Inc <support@audioscience.com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * The following is not a condition of use, merely a request: 762306a36Sopenharmony_ci * If you modify this program, particularly if you fix errors, AudioScience Inc 862306a36Sopenharmony_ci * would appreciate it if you grant us the right to use those modifications 962306a36Sopenharmony_ci * for any purpose including commercial applications. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "hpi_internal.h" 1362306a36Sopenharmony_ci#include "hpi_version.h" 1462306a36Sopenharmony_ci#include "hpimsginit.h" 1562306a36Sopenharmony_ci#include "hpioctl.h" 1662306a36Sopenharmony_ci#include "hpicmn.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci#include <linux/init.h> 2062306a36Sopenharmony_ci#include <linux/jiffies.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/time.h> 2362306a36Sopenharmony_ci#include <linux/wait.h> 2462306a36Sopenharmony_ci#include <linux/module.h> 2562306a36Sopenharmony_ci#include <sound/core.h> 2662306a36Sopenharmony_ci#include <sound/control.h> 2762306a36Sopenharmony_ci#include <sound/pcm.h> 2862306a36Sopenharmony_ci#include <sound/pcm_params.h> 2962306a36Sopenharmony_ci#include <sound/info.h> 3062306a36Sopenharmony_ci#include <sound/initval.h> 3162306a36Sopenharmony_ci#include <sound/tlv.h> 3262306a36Sopenharmony_ci#include <sound/hwdep.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3562306a36Sopenharmony_ciMODULE_AUTHOR("AudioScience inc. <support@audioscience.com>"); 3662306a36Sopenharmony_ciMODULE_DESCRIPTION("AudioScience ALSA ASI5xxx ASI6xxx ASI87xx ASI89xx " 3762306a36Sopenharmony_ci HPI_VER_STRING); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#if defined CONFIG_SND_DEBUG_VERBOSE 4062306a36Sopenharmony_ci/** 4162306a36Sopenharmony_ci * snd_printddd - very verbose debug printk 4262306a36Sopenharmony_ci * @format: format string 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * Works like snd_printk() for debugging purposes. 4562306a36Sopenharmony_ci * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set. 4662306a36Sopenharmony_ci * Must set snd module debug parameter to 3 to enable at runtime. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci#define snd_printddd(format, args...) \ 4962306a36Sopenharmony_ci __snd_printk(3, __FILE__, __LINE__, format, ##args) 5062306a36Sopenharmony_ci#else 5162306a36Sopenharmony_ci#define snd_printddd(format, args...) do { } while (0) 5262306a36Sopenharmony_ci#endif 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* index 0-MAX */ 5562306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ 5662306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 5762306a36Sopenharmony_cistatic bool enable_hpi_hwdep = 1; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 6062306a36Sopenharmony_ciMODULE_PARM_DESC(index, "ALSA index value for AudioScience soundcard."); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 6362306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ALSA ID string for AudioScience soundcard."); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 6662306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "ALSA enable AudioScience soundcard."); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cimodule_param(enable_hpi_hwdep, bool, 0644); 6962306a36Sopenharmony_ciMODULE_PARM_DESC(enable_hpi_hwdep, 7062306a36Sopenharmony_ci "ALSA enable HPI hwdep for AudioScience soundcard "); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* identify driver */ 7362306a36Sopenharmony_ci#ifdef KERNEL_ALSA_BUILD 7462306a36Sopenharmony_cistatic char *build_info = "Built using headers from kernel source"; 7562306a36Sopenharmony_cimodule_param(build_info, charp, 0444); 7662306a36Sopenharmony_ciMODULE_PARM_DESC(build_info, "Built using headers from kernel source"); 7762306a36Sopenharmony_ci#else 7862306a36Sopenharmony_cistatic char *build_info = "Built within ALSA source"; 7962306a36Sopenharmony_cimodule_param(build_info, charp, 0444); 8062306a36Sopenharmony_ciMODULE_PARM_DESC(build_info, "Built within ALSA source"); 8162306a36Sopenharmony_ci#endif 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* set to 1 to dump every control from adapter to log */ 8462306a36Sopenharmony_cistatic const int mixer_dump; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define DEFAULT_SAMPLERATE 44100 8762306a36Sopenharmony_cistatic int adapter_fs = DEFAULT_SAMPLERATE; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* defaults */ 9062306a36Sopenharmony_ci#define PERIODS_MIN 2 9162306a36Sopenharmony_ci#define PERIOD_BYTES_MIN 2048 9262306a36Sopenharmony_ci#define BUFFER_BYTES_MAX (512 * 1024) 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define MAX_CLOCKSOURCES (HPI_SAMPLECLOCK_SOURCE_LAST + 1 + 7) 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistruct clk_source { 9762306a36Sopenharmony_ci int source; 9862306a36Sopenharmony_ci int index; 9962306a36Sopenharmony_ci const char *name; 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct clk_cache { 10362306a36Sopenharmony_ci int count; 10462306a36Sopenharmony_ci int has_local; 10562306a36Sopenharmony_ci struct clk_source s[MAX_CLOCKSOURCES]; 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* Per card data */ 10962306a36Sopenharmony_cistruct snd_card_asihpi { 11062306a36Sopenharmony_ci struct snd_card *card; 11162306a36Sopenharmony_ci struct pci_dev *pci; 11262306a36Sopenharmony_ci struct hpi_adapter *hpi; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* In low latency mode there is only one stream, a pointer to its 11562306a36Sopenharmony_ci * private data is stored here on trigger and cleared on stop. 11662306a36Sopenharmony_ci * The interrupt handler uses it as a parameter when calling 11762306a36Sopenharmony_ci * snd_card_asihpi_timer_function(). 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci struct snd_card_asihpi_pcm *llmode_streampriv; 12062306a36Sopenharmony_ci void (*pcm_start)(struct snd_pcm_substream *substream); 12162306a36Sopenharmony_ci void (*pcm_stop)(struct snd_pcm_substream *substream); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci u32 h_mixer; 12462306a36Sopenharmony_ci struct clk_cache cc; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci u16 can_dma; 12762306a36Sopenharmony_ci u16 support_grouping; 12862306a36Sopenharmony_ci u16 support_mrx; 12962306a36Sopenharmony_ci u16 update_interval_frames; 13062306a36Sopenharmony_ci u16 in_max_chans; 13162306a36Sopenharmony_ci u16 out_max_chans; 13262306a36Sopenharmony_ci u16 in_min_chans; 13362306a36Sopenharmony_ci u16 out_min_chans; 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* Per stream data */ 13762306a36Sopenharmony_cistruct snd_card_asihpi_pcm { 13862306a36Sopenharmony_ci struct timer_list timer; 13962306a36Sopenharmony_ci unsigned int respawn_timer; 14062306a36Sopenharmony_ci unsigned int hpi_buffer_attached; 14162306a36Sopenharmony_ci unsigned int buffer_bytes; 14262306a36Sopenharmony_ci unsigned int period_bytes; 14362306a36Sopenharmony_ci unsigned int bytes_per_sec; 14462306a36Sopenharmony_ci unsigned int pcm_buf_host_rw_ofs; /* Host R/W pos */ 14562306a36Sopenharmony_ci unsigned int pcm_buf_dma_ofs; /* DMA R/W offset in buffer */ 14662306a36Sopenharmony_ci unsigned int pcm_buf_elapsed_dma_ofs; /* DMA R/W offset in buffer */ 14762306a36Sopenharmony_ci unsigned int drained_count; 14862306a36Sopenharmony_ci struct snd_pcm_substream *substream; 14962306a36Sopenharmony_ci u32 h_stream; 15062306a36Sopenharmony_ci struct hpi_format format; 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* universal stream verbs work with out or in stream handles */ 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/* Functions to allow driver to give a buffer to HPI for busmastering */ 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic u16 hpi_stream_host_buffer_attach( 15862306a36Sopenharmony_ci u32 h_stream, /* handle to outstream. */ 15962306a36Sopenharmony_ci u32 size_in_bytes, /* size in bytes of bus mastering buffer */ 16062306a36Sopenharmony_ci u32 pci_address 16162306a36Sopenharmony_ci) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct hpi_message hm; 16462306a36Sopenharmony_ci struct hpi_response hr; 16562306a36Sopenharmony_ci unsigned int obj = hpi_handle_object(h_stream); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (!h_stream) 16862306a36Sopenharmony_ci return HPI_ERROR_INVALID_OBJ; 16962306a36Sopenharmony_ci hpi_init_message_response(&hm, &hr, obj, 17062306a36Sopenharmony_ci obj == HPI_OBJ_OSTREAM ? 17162306a36Sopenharmony_ci HPI_OSTREAM_HOSTBUFFER_ALLOC : 17262306a36Sopenharmony_ci HPI_ISTREAM_HOSTBUFFER_ALLOC); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci hpi_handle_to_indexes(h_stream, &hm.adapter_index, 17562306a36Sopenharmony_ci &hm.obj_index); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci hm.u.d.u.buffer.buffer_size = size_in_bytes; 17862306a36Sopenharmony_ci hm.u.d.u.buffer.pci_address = pci_address; 17962306a36Sopenharmony_ci hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_GRANTADAPTER; 18062306a36Sopenharmony_ci hpi_send_recv(&hm, &hr); 18162306a36Sopenharmony_ci return hr.error; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic u16 hpi_stream_host_buffer_detach(u32 h_stream) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct hpi_message hm; 18762306a36Sopenharmony_ci struct hpi_response hr; 18862306a36Sopenharmony_ci unsigned int obj = hpi_handle_object(h_stream); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (!h_stream) 19162306a36Sopenharmony_ci return HPI_ERROR_INVALID_OBJ; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci hpi_init_message_response(&hm, &hr, obj, 19462306a36Sopenharmony_ci obj == HPI_OBJ_OSTREAM ? 19562306a36Sopenharmony_ci HPI_OSTREAM_HOSTBUFFER_FREE : 19662306a36Sopenharmony_ci HPI_ISTREAM_HOSTBUFFER_FREE); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci hpi_handle_to_indexes(h_stream, &hm.adapter_index, 19962306a36Sopenharmony_ci &hm.obj_index); 20062306a36Sopenharmony_ci hm.u.d.u.buffer.command = HPI_BUFFER_CMD_INTERNAL_REVOKEADAPTER; 20162306a36Sopenharmony_ci hpi_send_recv(&hm, &hr); 20262306a36Sopenharmony_ci return hr.error; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic inline u16 hpi_stream_start(u32 h_stream) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) 20862306a36Sopenharmony_ci return hpi_outstream_start(h_stream); 20962306a36Sopenharmony_ci else 21062306a36Sopenharmony_ci return hpi_instream_start(h_stream); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic inline u16 hpi_stream_stop(u32 h_stream) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) 21662306a36Sopenharmony_ci return hpi_outstream_stop(h_stream); 21762306a36Sopenharmony_ci else 21862306a36Sopenharmony_ci return hpi_instream_stop(h_stream); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic inline u16 hpi_stream_get_info_ex( 22262306a36Sopenharmony_ci u32 h_stream, 22362306a36Sopenharmony_ci u16 *pw_state, 22462306a36Sopenharmony_ci u32 *pbuffer_size, 22562306a36Sopenharmony_ci u32 *pdata_in_buffer, 22662306a36Sopenharmony_ci u32 *psample_count, 22762306a36Sopenharmony_ci u32 *pauxiliary_data 22862306a36Sopenharmony_ci) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci u16 e; 23162306a36Sopenharmony_ci if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) 23262306a36Sopenharmony_ci e = hpi_outstream_get_info_ex(h_stream, pw_state, 23362306a36Sopenharmony_ci pbuffer_size, pdata_in_buffer, 23462306a36Sopenharmony_ci psample_count, pauxiliary_data); 23562306a36Sopenharmony_ci else 23662306a36Sopenharmony_ci e = hpi_instream_get_info_ex(h_stream, pw_state, 23762306a36Sopenharmony_ci pbuffer_size, pdata_in_buffer, 23862306a36Sopenharmony_ci psample_count, pauxiliary_data); 23962306a36Sopenharmony_ci return e; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic inline u16 hpi_stream_group_add( 24362306a36Sopenharmony_ci u32 h_master, 24462306a36Sopenharmony_ci u32 h_stream) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci if (hpi_handle_object(h_master) == HPI_OBJ_OSTREAM) 24762306a36Sopenharmony_ci return hpi_outstream_group_add(h_master, h_stream); 24862306a36Sopenharmony_ci else 24962306a36Sopenharmony_ci return hpi_instream_group_add(h_master, h_stream); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic inline u16 hpi_stream_group_reset(u32 h_stream) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) 25562306a36Sopenharmony_ci return hpi_outstream_group_reset(h_stream); 25662306a36Sopenharmony_ci else 25762306a36Sopenharmony_ci return hpi_instream_group_reset(h_stream); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic u16 handle_error(u16 err, int line, char *filename) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci if (err) 26362306a36Sopenharmony_ci printk(KERN_WARNING 26462306a36Sopenharmony_ci "in file %s, line %d: HPI error %d\n", 26562306a36Sopenharmony_ci filename, line, err); 26662306a36Sopenharmony_ci return err; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci#define hpi_handle_error(x) handle_error(x, __LINE__, __FILE__) 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/***************************** GENERAL PCM ****************/ 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic void print_hwparams(struct snd_pcm_substream *substream, 27462306a36Sopenharmony_ci struct snd_pcm_hw_params *p) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci char name[16]; 27762306a36Sopenharmony_ci snd_pcm_debug_name(substream, name, sizeof(name)); 27862306a36Sopenharmony_ci snd_printdd("%s HWPARAMS\n", name); 27962306a36Sopenharmony_ci snd_printdd(" samplerate=%dHz channels=%d format=%d subformat=%d\n", 28062306a36Sopenharmony_ci params_rate(p), params_channels(p), 28162306a36Sopenharmony_ci params_format(p), params_subformat(p)); 28262306a36Sopenharmony_ci snd_printdd(" buffer=%dB period=%dB period_size=%dB periods=%d\n", 28362306a36Sopenharmony_ci params_buffer_bytes(p), params_period_bytes(p), 28462306a36Sopenharmony_ci params_period_size(p), params_periods(p)); 28562306a36Sopenharmony_ci snd_printdd(" buffer_size=%d access=%d data_rate=%dB/s\n", 28662306a36Sopenharmony_ci params_buffer_size(p), params_access(p), 28762306a36Sopenharmony_ci params_rate(p) * params_channels(p) * 28862306a36Sopenharmony_ci snd_pcm_format_width(params_format(p)) / 8); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci#define INVALID_FORMAT (__force snd_pcm_format_t)(-1) 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic const snd_pcm_format_t hpi_to_alsa_formats[] = { 29462306a36Sopenharmony_ci INVALID_FORMAT, /* INVALID */ 29562306a36Sopenharmony_ci SNDRV_PCM_FORMAT_U8, /* HPI_FORMAT_PCM8_UNSIGNED 1 */ 29662306a36Sopenharmony_ci SNDRV_PCM_FORMAT_S16, /* HPI_FORMAT_PCM16_SIGNED 2 */ 29762306a36Sopenharmony_ci INVALID_FORMAT, /* HPI_FORMAT_MPEG_L1 3 */ 29862306a36Sopenharmony_ci SNDRV_PCM_FORMAT_MPEG, /* HPI_FORMAT_MPEG_L2 4 */ 29962306a36Sopenharmony_ci SNDRV_PCM_FORMAT_MPEG, /* HPI_FORMAT_MPEG_L3 5 */ 30062306a36Sopenharmony_ci INVALID_FORMAT, /* HPI_FORMAT_DOLBY_AC2 6 */ 30162306a36Sopenharmony_ci INVALID_FORMAT, /* HPI_FORMAT_DOLBY_AC3 7 */ 30262306a36Sopenharmony_ci SNDRV_PCM_FORMAT_S16_BE,/* HPI_FORMAT_PCM16_BIGENDIAN 8 */ 30362306a36Sopenharmony_ci INVALID_FORMAT, /* HPI_FORMAT_AA_TAGIT1_HITS 9 */ 30462306a36Sopenharmony_ci INVALID_FORMAT, /* HPI_FORMAT_AA_TAGIT1_INSERTS 10 */ 30562306a36Sopenharmony_ci SNDRV_PCM_FORMAT_S32, /* HPI_FORMAT_PCM32_SIGNED 11 */ 30662306a36Sopenharmony_ci INVALID_FORMAT, /* HPI_FORMAT_RAW_BITSTREAM 12 */ 30762306a36Sopenharmony_ci INVALID_FORMAT, /* HPI_FORMAT_AA_TAGIT1_HITS_EX1 13 */ 30862306a36Sopenharmony_ci SNDRV_PCM_FORMAT_FLOAT, /* HPI_FORMAT_PCM32_FLOAT 14 */ 30962306a36Sopenharmony_ci#if 1 31062306a36Sopenharmony_ci /* ALSA can't handle 3 byte sample size together with power-of-2 31162306a36Sopenharmony_ci * constraint on buffer_bytes, so disable this format 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_ci INVALID_FORMAT 31462306a36Sopenharmony_ci#else 31562306a36Sopenharmony_ci /* SNDRV_PCM_FORMAT_S24_3LE */ /* HPI_FORMAT_PCM24_SIGNED 15 */ 31662306a36Sopenharmony_ci#endif 31762306a36Sopenharmony_ci}; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int snd_card_asihpi_format_alsa2hpi(snd_pcm_format_t alsa_format, 32162306a36Sopenharmony_ci u16 *hpi_format) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci u16 format; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci for (format = HPI_FORMAT_PCM8_UNSIGNED; 32662306a36Sopenharmony_ci format <= HPI_FORMAT_PCM24_SIGNED; format++) { 32762306a36Sopenharmony_ci if (hpi_to_alsa_formats[format] == alsa_format) { 32862306a36Sopenharmony_ci *hpi_format = format; 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci snd_printd(KERN_WARNING "failed match for alsa format %d\n", 33462306a36Sopenharmony_ci alsa_format); 33562306a36Sopenharmony_ci *hpi_format = 0; 33662306a36Sopenharmony_ci return -EINVAL; 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic void snd_card_asihpi_pcm_samplerates(struct snd_card_asihpi *asihpi, 34062306a36Sopenharmony_ci struct snd_pcm_hardware *pcmhw) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci u16 err; 34362306a36Sopenharmony_ci u32 h_control; 34462306a36Sopenharmony_ci u32 sample_rate; 34562306a36Sopenharmony_ci int idx; 34662306a36Sopenharmony_ci unsigned int rate_min = 200000; 34762306a36Sopenharmony_ci unsigned int rate_max = 0; 34862306a36Sopenharmony_ci unsigned int rates = 0; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (asihpi->support_mrx) { 35162306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_CONTINUOUS; 35262306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_8000_96000; 35362306a36Sopenharmony_ci rate_min = 8000; 35462306a36Sopenharmony_ci rate_max = 100000; 35562306a36Sopenharmony_ci } else { 35662306a36Sopenharmony_ci /* on cards without SRC, 35762306a36Sopenharmony_ci valid rates are determined by sampleclock */ 35862306a36Sopenharmony_ci err = hpi_mixer_get_control(asihpi->h_mixer, 35962306a36Sopenharmony_ci HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, 36062306a36Sopenharmony_ci HPI_CONTROL_SAMPLECLOCK, &h_control); 36162306a36Sopenharmony_ci if (err) { 36262306a36Sopenharmony_ci dev_err(&asihpi->pci->dev, 36362306a36Sopenharmony_ci "No local sampleclock, err %d\n", err); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci for (idx = -1; idx < 100; idx++) { 36762306a36Sopenharmony_ci if (idx == -1) { 36862306a36Sopenharmony_ci if (hpi_sample_clock_get_sample_rate(h_control, 36962306a36Sopenharmony_ci &sample_rate)) 37062306a36Sopenharmony_ci continue; 37162306a36Sopenharmony_ci } else if (hpi_sample_clock_query_local_rate(h_control, 37262306a36Sopenharmony_ci idx, &sample_rate)) { 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci rate_min = min(rate_min, sample_rate); 37762306a36Sopenharmony_ci rate_max = max(rate_max, sample_rate); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci switch (sample_rate) { 38062306a36Sopenharmony_ci case 5512: 38162306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_5512; 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci case 8000: 38462306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_8000; 38562306a36Sopenharmony_ci break; 38662306a36Sopenharmony_ci case 11025: 38762306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_11025; 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci case 16000: 39062306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_16000; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci case 22050: 39362306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_22050; 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci case 32000: 39662306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_32000; 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci case 44100: 39962306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_44100; 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci case 48000: 40262306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_48000; 40362306a36Sopenharmony_ci break; 40462306a36Sopenharmony_ci case 64000: 40562306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_64000; 40662306a36Sopenharmony_ci break; 40762306a36Sopenharmony_ci case 88200: 40862306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_88200; 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci case 96000: 41162306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_96000; 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci case 176400: 41462306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_176400; 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci case 192000: 41762306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_192000; 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci default: /* some other rate */ 42062306a36Sopenharmony_ci rates |= SNDRV_PCM_RATE_KNOT; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci pcmhw->rates = rates; 42662306a36Sopenharmony_ci pcmhw->rate_min = rate_min; 42762306a36Sopenharmony_ci pcmhw->rate_max = rate_max; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int snd_card_asihpi_pcm_hw_params(struct snd_pcm_substream *substream, 43162306a36Sopenharmony_ci struct snd_pcm_hw_params *params) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 43462306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 43562306a36Sopenharmony_ci struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); 43662306a36Sopenharmony_ci int err; 43762306a36Sopenharmony_ci u16 format; 43862306a36Sopenharmony_ci int width; 43962306a36Sopenharmony_ci unsigned int bytes_per_sec; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci print_hwparams(substream, params); 44262306a36Sopenharmony_ci err = snd_card_asihpi_format_alsa2hpi(params_format(params), &format); 44362306a36Sopenharmony_ci if (err) 44462306a36Sopenharmony_ci return err; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci hpi_handle_error(hpi_format_create(&dpcm->format, 44762306a36Sopenharmony_ci params_channels(params), 44862306a36Sopenharmony_ci format, params_rate(params), 0, 0)); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { 45162306a36Sopenharmony_ci if (hpi_instream_reset(dpcm->h_stream) != 0) 45262306a36Sopenharmony_ci return -EINVAL; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (hpi_instream_set_format( 45562306a36Sopenharmony_ci dpcm->h_stream, &dpcm->format) != 0) 45662306a36Sopenharmony_ci return -EINVAL; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci dpcm->hpi_buffer_attached = 0; 46062306a36Sopenharmony_ci if (card->can_dma) { 46162306a36Sopenharmony_ci err = hpi_stream_host_buffer_attach(dpcm->h_stream, 46262306a36Sopenharmony_ci params_buffer_bytes(params), runtime->dma_addr); 46362306a36Sopenharmony_ci if (err == 0) { 46462306a36Sopenharmony_ci snd_printdd( 46562306a36Sopenharmony_ci "stream_host_buffer_attach success %u %lu\n", 46662306a36Sopenharmony_ci params_buffer_bytes(params), 46762306a36Sopenharmony_ci (unsigned long)runtime->dma_addr); 46862306a36Sopenharmony_ci } else { 46962306a36Sopenharmony_ci snd_printd("stream_host_buffer_attach error %d\n", 47062306a36Sopenharmony_ci err); 47162306a36Sopenharmony_ci return -ENOMEM; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci err = hpi_stream_get_info_ex(dpcm->h_stream, NULL, 47562306a36Sopenharmony_ci &dpcm->hpi_buffer_attached, NULL, NULL, NULL); 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci bytes_per_sec = params_rate(params) * params_channels(params); 47862306a36Sopenharmony_ci width = snd_pcm_format_width(params_format(params)); 47962306a36Sopenharmony_ci bytes_per_sec *= width; 48062306a36Sopenharmony_ci bytes_per_sec /= 8; 48162306a36Sopenharmony_ci if (width < 0 || bytes_per_sec == 0) 48262306a36Sopenharmony_ci return -EINVAL; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci dpcm->bytes_per_sec = bytes_per_sec; 48562306a36Sopenharmony_ci dpcm->buffer_bytes = params_buffer_bytes(params); 48662306a36Sopenharmony_ci dpcm->period_bytes = params_period_bytes(params); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return 0; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int 49262306a36Sopenharmony_cisnd_card_asihpi_hw_free(struct snd_pcm_substream *substream) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 49562306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 49662306a36Sopenharmony_ci if (dpcm->hpi_buffer_attached) 49762306a36Sopenharmony_ci hpi_stream_host_buffer_detach(dpcm->h_stream); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return 0; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic void snd_card_asihpi_runtime_free(struct snd_pcm_runtime *runtime) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 50562306a36Sopenharmony_ci kfree(dpcm); 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_cistatic void snd_card_asihpi_pcm_timer_start(struct snd_pcm_substream * 50962306a36Sopenharmony_ci substream) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 51262306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 51362306a36Sopenharmony_ci int expiry; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci expiry = HZ / 200; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci expiry = max(expiry, 1); /* don't let it be zero! */ 51862306a36Sopenharmony_ci mod_timer(&dpcm->timer, jiffies + expiry); 51962306a36Sopenharmony_ci dpcm->respawn_timer = 1; 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic void snd_card_asihpi_pcm_timer_stop(struct snd_pcm_substream *substream) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 52562306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci dpcm->respawn_timer = 0; 52862306a36Sopenharmony_ci del_timer(&dpcm->timer); 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm; 53462306a36Sopenharmony_ci struct snd_card_asihpi *card; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci dpcm = (struct snd_card_asihpi_pcm *)substream->runtime->private_data; 53762306a36Sopenharmony_ci card = snd_pcm_substream_chip(substream); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci WARN_ON(in_interrupt()); 54062306a36Sopenharmony_ci card->llmode_streampriv = dpcm; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index, 54362306a36Sopenharmony_ci HPI_ADAPTER_PROPERTY_IRQ_RATE, 54462306a36Sopenharmony_ci card->update_interval_frames, 0)); 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct snd_card_asihpi *card; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci card = snd_pcm_substream_chip(substream); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index, 55462306a36Sopenharmony_ci HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0)); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci card->llmode_streampriv = NULL; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, 56062306a36Sopenharmony_ci int cmd) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data; 56362306a36Sopenharmony_ci struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); 56462306a36Sopenharmony_ci struct snd_pcm_substream *s; 56562306a36Sopenharmony_ci u16 e; 56662306a36Sopenharmony_ci char name[16]; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci snd_pcm_debug_name(substream, name, sizeof(name)); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci switch (cmd) { 57162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 57262306a36Sopenharmony_ci snd_printdd("%s trigger start\n", name); 57362306a36Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 57462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = s->runtime; 57562306a36Sopenharmony_ci struct snd_card_asihpi_pcm *ds = runtime->private_data; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (snd_pcm_substream_chip(s) != card) 57862306a36Sopenharmony_ci continue; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* don't link Cap and Play */ 58162306a36Sopenharmony_ci if (substream->stream != s->stream) 58262306a36Sopenharmony_ci continue; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci ds->drained_count = 0; 58562306a36Sopenharmony_ci if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { 58662306a36Sopenharmony_ci /* How do I know how much valid data is present 58762306a36Sopenharmony_ci * in buffer? Must be at least one period! 58862306a36Sopenharmony_ci * Guessing 2 periods, but if 58962306a36Sopenharmony_ci * buffer is bigger it may contain even more 59062306a36Sopenharmony_ci * data?? 59162306a36Sopenharmony_ci */ 59262306a36Sopenharmony_ci unsigned int preload = ds->period_bytes * 1; 59362306a36Sopenharmony_ci snd_printddd("%d preload %d\n", s->number, preload); 59462306a36Sopenharmony_ci hpi_handle_error(hpi_outstream_write_buf( 59562306a36Sopenharmony_ci ds->h_stream, 59662306a36Sopenharmony_ci &runtime->dma_area[0], 59762306a36Sopenharmony_ci preload, 59862306a36Sopenharmony_ci &ds->format)); 59962306a36Sopenharmony_ci ds->pcm_buf_host_rw_ofs = preload; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (card->support_grouping) { 60362306a36Sopenharmony_ci snd_printdd("%d group\n", s->number); 60462306a36Sopenharmony_ci e = hpi_stream_group_add( 60562306a36Sopenharmony_ci dpcm->h_stream, 60662306a36Sopenharmony_ci ds->h_stream); 60762306a36Sopenharmony_ci if (!e) { 60862306a36Sopenharmony_ci snd_pcm_trigger_done(s, substream); 60962306a36Sopenharmony_ci } else { 61062306a36Sopenharmony_ci hpi_handle_error(e); 61162306a36Sopenharmony_ci break; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci } else 61462306a36Sopenharmony_ci break; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci /* start the master stream */ 61762306a36Sopenharmony_ci card->pcm_start(substream); 61862306a36Sopenharmony_ci if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) || 61962306a36Sopenharmony_ci !card->can_dma) 62062306a36Sopenharmony_ci hpi_handle_error(hpi_stream_start(dpcm->h_stream)); 62162306a36Sopenharmony_ci break; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 62462306a36Sopenharmony_ci snd_printdd("%s trigger stop\n", name); 62562306a36Sopenharmony_ci card->pcm_stop(substream); 62662306a36Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 62762306a36Sopenharmony_ci if (snd_pcm_substream_chip(s) != card) 62862306a36Sopenharmony_ci continue; 62962306a36Sopenharmony_ci /* don't link Cap and Play */ 63062306a36Sopenharmony_ci if (substream->stream != s->stream) 63162306a36Sopenharmony_ci continue; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /*? workaround linked streams don't 63462306a36Sopenharmony_ci transition to SETUP 20070706*/ 63562306a36Sopenharmony_ci __snd_pcm_set_state(s->runtime, SNDRV_PCM_STATE_SETUP); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (card->support_grouping) { 63862306a36Sopenharmony_ci snd_printdd("%d group\n", s->number); 63962306a36Sopenharmony_ci snd_pcm_trigger_done(s, substream); 64062306a36Sopenharmony_ci } else 64162306a36Sopenharmony_ci break; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci /* _prepare and _hwparams reset the stream */ 64562306a36Sopenharmony_ci hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); 64662306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 64762306a36Sopenharmony_ci hpi_handle_error( 64862306a36Sopenharmony_ci hpi_outstream_reset(dpcm->h_stream)); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (card->support_grouping) 65162306a36Sopenharmony_ci hpi_handle_error(hpi_stream_group_reset(dpcm->h_stream)); 65262306a36Sopenharmony_ci break; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 65562306a36Sopenharmony_ci snd_printdd("%s trigger pause release\n", name); 65662306a36Sopenharmony_ci card->pcm_start(substream); 65762306a36Sopenharmony_ci hpi_handle_error(hpi_stream_start(dpcm->h_stream)); 65862306a36Sopenharmony_ci break; 65962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 66062306a36Sopenharmony_ci snd_printdd("%s trigger pause push\n", name); 66162306a36Sopenharmony_ci card->pcm_stop(substream); 66262306a36Sopenharmony_ci hpi_handle_error(hpi_stream_stop(dpcm->h_stream)); 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci default: 66562306a36Sopenharmony_ci snd_printd(KERN_ERR "\tINVALID\n"); 66662306a36Sopenharmony_ci return -EINVAL; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci return 0; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci/*algorithm outline 67362306a36Sopenharmony_ci Without linking degenerates to getting single stream pos etc 67462306a36Sopenharmony_ci Without mmap 2nd loop degenerates to snd_pcm_period_elapsed 67562306a36Sopenharmony_ci*/ 67662306a36Sopenharmony_ci/* 67762306a36Sopenharmony_cipcm_buf_dma_ofs=get_buf_pos(s); 67862306a36Sopenharmony_cifor_each_linked_stream(s) { 67962306a36Sopenharmony_ci pcm_buf_dma_ofs=get_buf_pos(s); 68062306a36Sopenharmony_ci min_buf_pos = modulo_min(min_buf_pos, pcm_buf_dma_ofs, buffer_bytes) 68162306a36Sopenharmony_ci new_data = min(new_data, calc_new_data(pcm_buf_dma_ofs,irq_pos) 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_citimer.expires = jiffies + predict_next_period_ready(min_buf_pos); 68462306a36Sopenharmony_cifor_each_linked_stream(s) { 68562306a36Sopenharmony_ci s->pcm_buf_dma_ofs = min_buf_pos; 68662306a36Sopenharmony_ci if (new_data > period_bytes) { 68762306a36Sopenharmony_ci if (mmap) { 68862306a36Sopenharmony_ci irq_pos = (irq_pos + period_bytes) % buffer_bytes; 68962306a36Sopenharmony_ci if (playback) { 69062306a36Sopenharmony_ci write(period_bytes); 69162306a36Sopenharmony_ci } else { 69262306a36Sopenharmony_ci read(period_bytes); 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci snd_pcm_period_elapsed(s); 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci*/ 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci/** Minimum of 2 modulo values. Works correctly when the difference between 70162306a36Sopenharmony_ci* the values is less than half the modulus 70262306a36Sopenharmony_ci*/ 70362306a36Sopenharmony_cistatic inline unsigned int modulo_min(unsigned int a, unsigned int b, 70462306a36Sopenharmony_ci unsigned long int modulus) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci unsigned int result; 70762306a36Sopenharmony_ci if (((a-b) % modulus) < (modulus/2)) 70862306a36Sopenharmony_ci result = b; 70962306a36Sopenharmony_ci else 71062306a36Sopenharmony_ci result = a; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci return result; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci/** Timer function, equivalent to interrupt service routine for cards 71662306a36Sopenharmony_ci*/ 71762306a36Sopenharmony_cistatic void snd_card_asihpi_timer_function(struct timer_list *t) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = from_timer(dpcm, t, timer); 72062306a36Sopenharmony_ci struct snd_pcm_substream *substream = dpcm->substream; 72162306a36Sopenharmony_ci struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); 72262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime; 72362306a36Sopenharmony_ci struct snd_pcm_substream *s; 72462306a36Sopenharmony_ci unsigned int newdata = 0; 72562306a36Sopenharmony_ci unsigned int pcm_buf_dma_ofs, min_buf_pos = 0; 72662306a36Sopenharmony_ci unsigned int remdata, xfercount, next_jiffies; 72762306a36Sopenharmony_ci int first = 1; 72862306a36Sopenharmony_ci u16 state; 72962306a36Sopenharmony_ci u32 buffer_size, bytes_avail, samples_played, on_card_bytes; 73062306a36Sopenharmony_ci char name[16]; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci snd_pcm_debug_name(substream, name, sizeof(name)); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* find minimum newdata and buffer pos in group */ 73662306a36Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 73762306a36Sopenharmony_ci struct snd_card_asihpi_pcm *ds = s->runtime->private_data; 73862306a36Sopenharmony_ci runtime = s->runtime; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (snd_pcm_substream_chip(s) != card) 74162306a36Sopenharmony_ci continue; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci /* don't link Cap and Play */ 74462306a36Sopenharmony_ci if (substream->stream != s->stream) 74562306a36Sopenharmony_ci continue; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci hpi_handle_error(hpi_stream_get_info_ex( 74862306a36Sopenharmony_ci ds->h_stream, &state, 74962306a36Sopenharmony_ci &buffer_size, &bytes_avail, 75062306a36Sopenharmony_ci &samples_played, &on_card_bytes)); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* number of bytes in on-card buffer */ 75362306a36Sopenharmony_ci runtime->delay = on_card_bytes; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (!card->can_dma) 75662306a36Sopenharmony_ci on_card_bytes = bytes_avail; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { 75962306a36Sopenharmony_ci pcm_buf_dma_ofs = ds->pcm_buf_host_rw_ofs - bytes_avail; 76062306a36Sopenharmony_ci if (state == HPI_STATE_STOPPED) { 76162306a36Sopenharmony_ci if (bytes_avail == 0) { 76262306a36Sopenharmony_ci hpi_handle_error(hpi_stream_start(ds->h_stream)); 76362306a36Sopenharmony_ci snd_printdd("P%d start\n", s->number); 76462306a36Sopenharmony_ci ds->drained_count = 0; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci } else if (state == HPI_STATE_DRAINED) { 76762306a36Sopenharmony_ci snd_printd(KERN_WARNING "P%d drained\n", 76862306a36Sopenharmony_ci s->number); 76962306a36Sopenharmony_ci ds->drained_count++; 77062306a36Sopenharmony_ci if (ds->drained_count > 20) { 77162306a36Sopenharmony_ci snd_pcm_stop_xrun(s); 77262306a36Sopenharmony_ci continue; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci } else { 77562306a36Sopenharmony_ci ds->drained_count = 0; 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci } else 77862306a36Sopenharmony_ci pcm_buf_dma_ofs = bytes_avail + ds->pcm_buf_host_rw_ofs; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (first) { 78162306a36Sopenharmony_ci /* can't statically init min when wrap is involved */ 78262306a36Sopenharmony_ci min_buf_pos = pcm_buf_dma_ofs; 78362306a36Sopenharmony_ci newdata = (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes; 78462306a36Sopenharmony_ci first = 0; 78562306a36Sopenharmony_ci } else { 78662306a36Sopenharmony_ci min_buf_pos = 78762306a36Sopenharmony_ci modulo_min(min_buf_pos, pcm_buf_dma_ofs, UINT_MAX+1L); 78862306a36Sopenharmony_ci newdata = min( 78962306a36Sopenharmony_ci (pcm_buf_dma_ofs - ds->pcm_buf_elapsed_dma_ofs) % ds->buffer_bytes, 79062306a36Sopenharmony_ci newdata); 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci snd_printddd( 79462306a36Sopenharmony_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", 79562306a36Sopenharmony_ci name, s->number, state, 79662306a36Sopenharmony_ci ds->pcm_buf_elapsed_dma_ofs, 79762306a36Sopenharmony_ci ds->pcm_buf_host_rw_ofs, 79862306a36Sopenharmony_ci pcm_buf_dma_ofs, 79962306a36Sopenharmony_ci (int)bytes_avail, 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci (int)on_card_bytes, 80262306a36Sopenharmony_ci buffer_size-bytes_avail, 80362306a36Sopenharmony_ci (unsigned long)frames_to_bytes(runtime, 80462306a36Sopenharmony_ci runtime->status->hw_ptr), 80562306a36Sopenharmony_ci (unsigned long)frames_to_bytes(runtime, 80662306a36Sopenharmony_ci runtime->control->appl_ptr) 80762306a36Sopenharmony_ci ); 80862306a36Sopenharmony_ci } 80962306a36Sopenharmony_ci pcm_buf_dma_ofs = min_buf_pos; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci remdata = newdata % dpcm->period_bytes; 81262306a36Sopenharmony_ci xfercount = newdata - remdata; /* a multiple of period_bytes */ 81362306a36Sopenharmony_ci /* come back when on_card_bytes has decreased enough to allow 81462306a36Sopenharmony_ci write to happen, or when data has been consumed to make another 81562306a36Sopenharmony_ci period 81662306a36Sopenharmony_ci */ 81762306a36Sopenharmony_ci if (xfercount && (on_card_bytes > dpcm->period_bytes)) 81862306a36Sopenharmony_ci next_jiffies = ((on_card_bytes - dpcm->period_bytes) * HZ / dpcm->bytes_per_sec); 81962306a36Sopenharmony_ci else 82062306a36Sopenharmony_ci next_jiffies = ((dpcm->period_bytes - remdata) * HZ / dpcm->bytes_per_sec); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci next_jiffies = max(next_jiffies, 1U); 82362306a36Sopenharmony_ci dpcm->timer.expires = jiffies + next_jiffies; 82462306a36Sopenharmony_ci snd_printddd("timer2, jif=%d, buf_pos=%d, newdata=%d, xfer=%d\n", 82562306a36Sopenharmony_ci next_jiffies, pcm_buf_dma_ofs, newdata, xfercount); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci snd_pcm_group_for_each_entry(s, substream) { 82862306a36Sopenharmony_ci struct snd_card_asihpi_pcm *ds = s->runtime->private_data; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci /* don't link Cap and Play */ 83162306a36Sopenharmony_ci if (substream->stream != s->stream) 83262306a36Sopenharmony_ci continue; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci /* Store dma offset for use by pointer callback */ 83562306a36Sopenharmony_ci ds->pcm_buf_dma_ofs = pcm_buf_dma_ofs; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (xfercount && 83862306a36Sopenharmony_ci /* Limit use of on card fifo for playback */ 83962306a36Sopenharmony_ci ((on_card_bytes <= ds->period_bytes) || 84062306a36Sopenharmony_ci (s->stream == SNDRV_PCM_STREAM_CAPTURE))) 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci { 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci unsigned int buf_ofs = ds->pcm_buf_host_rw_ofs % ds->buffer_bytes; 84562306a36Sopenharmony_ci unsigned int xfer1, xfer2; 84662306a36Sopenharmony_ci char *pd = &s->runtime->dma_area[buf_ofs]; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (card->can_dma) { /* buffer wrap is handled at lower level */ 84962306a36Sopenharmony_ci xfer1 = xfercount; 85062306a36Sopenharmony_ci xfer2 = 0; 85162306a36Sopenharmony_ci } else { 85262306a36Sopenharmony_ci xfer1 = min(xfercount, ds->buffer_bytes - buf_ofs); 85362306a36Sopenharmony_ci xfer2 = xfercount - xfer1; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) { 85762306a36Sopenharmony_ci snd_printddd("write1, P=%d, xfer=%d, buf_ofs=%d\n", 85862306a36Sopenharmony_ci s->number, xfer1, buf_ofs); 85962306a36Sopenharmony_ci hpi_handle_error( 86062306a36Sopenharmony_ci hpi_outstream_write_buf( 86162306a36Sopenharmony_ci ds->h_stream, pd, xfer1, 86262306a36Sopenharmony_ci &ds->format)); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (xfer2) { 86562306a36Sopenharmony_ci pd = s->runtime->dma_area; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci snd_printddd("write2, P=%d, xfer=%d, buf_ofs=%d\n", 86862306a36Sopenharmony_ci s->number, 86962306a36Sopenharmony_ci xfercount - xfer1, buf_ofs); 87062306a36Sopenharmony_ci hpi_handle_error( 87162306a36Sopenharmony_ci hpi_outstream_write_buf( 87262306a36Sopenharmony_ci ds->h_stream, pd, 87362306a36Sopenharmony_ci xfercount - xfer1, 87462306a36Sopenharmony_ci &ds->format)); 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci } else { 87762306a36Sopenharmony_ci snd_printddd("read1, C=%d, xfer=%d\n", 87862306a36Sopenharmony_ci s->number, xfer1); 87962306a36Sopenharmony_ci hpi_handle_error( 88062306a36Sopenharmony_ci hpi_instream_read_buf( 88162306a36Sopenharmony_ci ds->h_stream, 88262306a36Sopenharmony_ci pd, xfer1)); 88362306a36Sopenharmony_ci if (xfer2) { 88462306a36Sopenharmony_ci pd = s->runtime->dma_area; 88562306a36Sopenharmony_ci snd_printddd("read2, C=%d, xfer=%d\n", 88662306a36Sopenharmony_ci s->number, xfer2); 88762306a36Sopenharmony_ci hpi_handle_error( 88862306a36Sopenharmony_ci hpi_instream_read_buf( 88962306a36Sopenharmony_ci ds->h_stream, 89062306a36Sopenharmony_ci pd, xfer2)); 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci /* ? host_rw_ofs always ahead of elapsed_dma_ofs by preload size? */ 89462306a36Sopenharmony_ci ds->pcm_buf_host_rw_ofs += xfercount; 89562306a36Sopenharmony_ci ds->pcm_buf_elapsed_dma_ofs += xfercount; 89662306a36Sopenharmony_ci snd_pcm_period_elapsed(s); 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci if (!card->hpi->interrupt_mode && dpcm->respawn_timer) 90162306a36Sopenharmony_ci add_timer(&dpcm->timer); 90262306a36Sopenharmony_ci} 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_cistatic void snd_card_asihpi_isr(struct hpi_adapter *a) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct snd_card_asihpi *asihpi; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci WARN_ON(!a || !a->snd_card || !a->snd_card->private_data); 90962306a36Sopenharmony_ci asihpi = (struct snd_card_asihpi *)a->snd_card->private_data; 91062306a36Sopenharmony_ci if (asihpi->llmode_streampriv) 91162306a36Sopenharmony_ci snd_card_asihpi_timer_function( 91262306a36Sopenharmony_ci &asihpi->llmode_streampriv->timer); 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci/***************************** PLAYBACK OPS ****************/ 91662306a36Sopenharmony_cistatic int snd_card_asihpi_playback_prepare(struct snd_pcm_substream * 91762306a36Sopenharmony_ci substream) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 92062306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci snd_printdd("P%d prepare\n", substream->number); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci hpi_handle_error(hpi_outstream_reset(dpcm->h_stream)); 92562306a36Sopenharmony_ci dpcm->pcm_buf_host_rw_ofs = 0; 92662306a36Sopenharmony_ci dpcm->pcm_buf_dma_ofs = 0; 92762306a36Sopenharmony_ci dpcm->pcm_buf_elapsed_dma_ofs = 0; 92862306a36Sopenharmony_ci return 0; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic snd_pcm_uframes_t 93262306a36Sopenharmony_cisnd_card_asihpi_playback_pointer(struct snd_pcm_substream *substream) 93362306a36Sopenharmony_ci{ 93462306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 93562306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 93662306a36Sopenharmony_ci snd_pcm_uframes_t ptr; 93762306a36Sopenharmony_ci char name[16]; 93862306a36Sopenharmony_ci snd_pcm_debug_name(substream, name, sizeof(name)); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci ptr = bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes); 94162306a36Sopenharmony_ci snd_printddd("%s, pointer=%ld\n", name, (unsigned long)ptr); 94262306a36Sopenharmony_ci return ptr; 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_cistatic u64 snd_card_asihpi_playback_formats(struct snd_card_asihpi *asihpi, 94662306a36Sopenharmony_ci u32 h_stream) 94762306a36Sopenharmony_ci{ 94862306a36Sopenharmony_ci struct hpi_format hpi_format; 94962306a36Sopenharmony_ci u16 format; 95062306a36Sopenharmony_ci u16 err; 95162306a36Sopenharmony_ci u32 h_control; 95262306a36Sopenharmony_ci u32 sample_rate = 48000; 95362306a36Sopenharmony_ci u64 formats = 0; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci /* on cards without SRC, must query at valid rate, 95662306a36Sopenharmony_ci * maybe set by external sync 95762306a36Sopenharmony_ci */ 95862306a36Sopenharmony_ci err = hpi_mixer_get_control(asihpi->h_mixer, 95962306a36Sopenharmony_ci HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, 96062306a36Sopenharmony_ci HPI_CONTROL_SAMPLECLOCK, &h_control); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (!err) 96362306a36Sopenharmony_ci err = hpi_sample_clock_get_sample_rate(h_control, 96462306a36Sopenharmony_ci &sample_rate); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci for (format = HPI_FORMAT_PCM8_UNSIGNED; 96762306a36Sopenharmony_ci format <= HPI_FORMAT_PCM24_SIGNED; format++) { 96862306a36Sopenharmony_ci err = hpi_format_create(&hpi_format, asihpi->out_max_chans, 96962306a36Sopenharmony_ci format, sample_rate, 128000, 0); 97062306a36Sopenharmony_ci if (!err) 97162306a36Sopenharmony_ci err = hpi_outstream_query_format(h_stream, &hpi_format); 97262306a36Sopenharmony_ci if (!err && (hpi_to_alsa_formats[format] != INVALID_FORMAT)) 97362306a36Sopenharmony_ci formats |= pcm_format_to_bits(hpi_to_alsa_formats[format]); 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci return formats; 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_cistatic int snd_card_asihpi_playback_open(struct snd_pcm_substream *substream) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 98162306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm; 98262306a36Sopenharmony_ci struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); 98362306a36Sopenharmony_ci struct snd_pcm_hardware snd_card_asihpi_playback; 98462306a36Sopenharmony_ci int err; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); 98762306a36Sopenharmony_ci if (dpcm == NULL) 98862306a36Sopenharmony_ci return -ENOMEM; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci err = hpi_outstream_open(card->hpi->adapter->index, 99162306a36Sopenharmony_ci substream->number, &dpcm->h_stream); 99262306a36Sopenharmony_ci hpi_handle_error(err); 99362306a36Sopenharmony_ci if (err) 99462306a36Sopenharmony_ci kfree(dpcm); 99562306a36Sopenharmony_ci if (err == HPI_ERROR_OBJ_ALREADY_OPEN) 99662306a36Sopenharmony_ci return -EBUSY; 99762306a36Sopenharmony_ci if (err) 99862306a36Sopenharmony_ci return -EIO; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci /*? also check ASI5000 samplerate source 100162306a36Sopenharmony_ci If external, only support external rate. 100262306a36Sopenharmony_ci If internal and other stream playing, can't switch 100362306a36Sopenharmony_ci */ 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0); 100662306a36Sopenharmony_ci dpcm->substream = substream; 100762306a36Sopenharmony_ci runtime->private_data = dpcm; 100862306a36Sopenharmony_ci runtime->private_free = snd_card_asihpi_runtime_free; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci memset(&snd_card_asihpi_playback, 0, sizeof(snd_card_asihpi_playback)); 101162306a36Sopenharmony_ci if (!card->hpi->interrupt_mode) { 101262306a36Sopenharmony_ci snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX; 101362306a36Sopenharmony_ci snd_card_asihpi_playback.period_bytes_min = PERIOD_BYTES_MIN; 101462306a36Sopenharmony_ci snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; 101562306a36Sopenharmony_ci snd_card_asihpi_playback.periods_min = PERIODS_MIN; 101662306a36Sopenharmony_ci snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN; 101762306a36Sopenharmony_ci } else { 101862306a36Sopenharmony_ci size_t pbmin = card->update_interval_frames * 101962306a36Sopenharmony_ci card->out_max_chans; 102062306a36Sopenharmony_ci snd_card_asihpi_playback.buffer_bytes_max = BUFFER_BYTES_MAX; 102162306a36Sopenharmony_ci snd_card_asihpi_playback.period_bytes_min = pbmin; 102262306a36Sopenharmony_ci snd_card_asihpi_playback.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; 102362306a36Sopenharmony_ci snd_card_asihpi_playback.periods_min = PERIODS_MIN; 102462306a36Sopenharmony_ci snd_card_asihpi_playback.periods_max = BUFFER_BYTES_MAX / pbmin; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci /* snd_card_asihpi_playback.fifo_size = 0; */ 102862306a36Sopenharmony_ci snd_card_asihpi_playback.channels_max = card->out_max_chans; 102962306a36Sopenharmony_ci snd_card_asihpi_playback.channels_min = card->out_min_chans; 103062306a36Sopenharmony_ci snd_card_asihpi_playback.formats = 103162306a36Sopenharmony_ci snd_card_asihpi_playback_formats(card, dpcm->h_stream); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_playback); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci snd_card_asihpi_playback.info = SNDRV_PCM_INFO_INTERLEAVED | 103662306a36Sopenharmony_ci SNDRV_PCM_INFO_DOUBLE | 103762306a36Sopenharmony_ci SNDRV_PCM_INFO_BATCH | 103862306a36Sopenharmony_ci SNDRV_PCM_INFO_BLOCK_TRANSFER | 103962306a36Sopenharmony_ci SNDRV_PCM_INFO_PAUSE | 104062306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 104162306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci if (card->support_grouping) { 104462306a36Sopenharmony_ci snd_card_asihpi_playback.info |= SNDRV_PCM_INFO_SYNC_START; 104562306a36Sopenharmony_ci snd_pcm_set_sync(substream); 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci /* struct is copied, so can create initializer dynamically */ 104962306a36Sopenharmony_ci runtime->hw = snd_card_asihpi_playback; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci if (card->can_dma) 105262306a36Sopenharmony_ci err = snd_pcm_hw_constraint_pow2(runtime, 0, 105362306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_BYTES); 105462306a36Sopenharmony_ci if (err < 0) 105562306a36Sopenharmony_ci return err; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 105862306a36Sopenharmony_ci card->update_interval_frames); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 106162306a36Sopenharmony_ci card->update_interval_frames, UINT_MAX); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci snd_printdd("playback open\n"); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci return 0; 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cistatic int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 107162306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci hpi_handle_error(hpi_outstream_close(dpcm->h_stream)); 107462306a36Sopenharmony_ci snd_printdd("playback close\n"); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci return 0; 107762306a36Sopenharmony_ci} 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = { 108062306a36Sopenharmony_ci .open = snd_card_asihpi_playback_open, 108162306a36Sopenharmony_ci .close = snd_card_asihpi_playback_close, 108262306a36Sopenharmony_ci .hw_params = snd_card_asihpi_pcm_hw_params, 108362306a36Sopenharmony_ci .hw_free = snd_card_asihpi_hw_free, 108462306a36Sopenharmony_ci .prepare = snd_card_asihpi_playback_prepare, 108562306a36Sopenharmony_ci .trigger = snd_card_asihpi_trigger, 108662306a36Sopenharmony_ci .pointer = snd_card_asihpi_playback_pointer, 108762306a36Sopenharmony_ci}; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci/***************************** CAPTURE OPS ****************/ 109062306a36Sopenharmony_cistatic snd_pcm_uframes_t 109162306a36Sopenharmony_cisnd_card_asihpi_capture_pointer(struct snd_pcm_substream *substream) 109262306a36Sopenharmony_ci{ 109362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 109462306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 109562306a36Sopenharmony_ci char name[16]; 109662306a36Sopenharmony_ci snd_pcm_debug_name(substream, name, sizeof(name)); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci snd_printddd("%s, pointer=%d\n", name, dpcm->pcm_buf_dma_ofs); 109962306a36Sopenharmony_ci /* NOTE Unlike playback can't use actual samples_played 110062306a36Sopenharmony_ci for the capture position, because those samples aren't yet in 110162306a36Sopenharmony_ci the local buffer available for reading. 110262306a36Sopenharmony_ci */ 110362306a36Sopenharmony_ci return bytes_to_frames(runtime, dpcm->pcm_buf_dma_ofs % dpcm->buffer_bytes); 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_cistatic int snd_card_asihpi_capture_prepare(struct snd_pcm_substream *substream) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 110962306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = runtime->private_data; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci hpi_handle_error(hpi_instream_reset(dpcm->h_stream)); 111262306a36Sopenharmony_ci dpcm->pcm_buf_host_rw_ofs = 0; 111362306a36Sopenharmony_ci dpcm->pcm_buf_dma_ofs = 0; 111462306a36Sopenharmony_ci dpcm->pcm_buf_elapsed_dma_ofs = 0; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci snd_printdd("Capture Prepare %d\n", substream->number); 111762306a36Sopenharmony_ci return 0; 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic u64 snd_card_asihpi_capture_formats(struct snd_card_asihpi *asihpi, 112162306a36Sopenharmony_ci u32 h_stream) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci struct hpi_format hpi_format; 112462306a36Sopenharmony_ci u16 format; 112562306a36Sopenharmony_ci u16 err; 112662306a36Sopenharmony_ci u32 h_control; 112762306a36Sopenharmony_ci u32 sample_rate = 48000; 112862306a36Sopenharmony_ci u64 formats = 0; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci /* on cards without SRC, must query at valid rate, 113162306a36Sopenharmony_ci maybe set by external sync */ 113262306a36Sopenharmony_ci err = hpi_mixer_get_control(asihpi->h_mixer, 113362306a36Sopenharmony_ci HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, 113462306a36Sopenharmony_ci HPI_CONTROL_SAMPLECLOCK, &h_control); 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci if (!err) 113762306a36Sopenharmony_ci err = hpi_sample_clock_get_sample_rate(h_control, 113862306a36Sopenharmony_ci &sample_rate); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci for (format = HPI_FORMAT_PCM8_UNSIGNED; 114162306a36Sopenharmony_ci format <= HPI_FORMAT_PCM24_SIGNED; format++) { 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci err = hpi_format_create(&hpi_format, asihpi->in_max_chans, 114462306a36Sopenharmony_ci format, sample_rate, 128000, 0); 114562306a36Sopenharmony_ci if (!err) 114662306a36Sopenharmony_ci err = hpi_instream_query_format(h_stream, &hpi_format); 114762306a36Sopenharmony_ci if (!err && (hpi_to_alsa_formats[format] != INVALID_FORMAT)) 114862306a36Sopenharmony_ci formats |= pcm_format_to_bits(hpi_to_alsa_formats[format]); 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci return formats; 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic int snd_card_asihpi_capture_open(struct snd_pcm_substream *substream) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 115662306a36Sopenharmony_ci struct snd_card_asihpi *card = snd_pcm_substream_chip(substream); 115762306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm; 115862306a36Sopenharmony_ci struct snd_pcm_hardware snd_card_asihpi_capture; 115962306a36Sopenharmony_ci int err; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); 116262306a36Sopenharmony_ci if (dpcm == NULL) 116362306a36Sopenharmony_ci return -ENOMEM; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci snd_printdd("capture open adapter %d stream %d\n", 116662306a36Sopenharmony_ci card->hpi->adapter->index, substream->number); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci err = hpi_handle_error( 116962306a36Sopenharmony_ci hpi_instream_open(card->hpi->adapter->index, 117062306a36Sopenharmony_ci substream->number, &dpcm->h_stream)); 117162306a36Sopenharmony_ci if (err) 117262306a36Sopenharmony_ci kfree(dpcm); 117362306a36Sopenharmony_ci if (err == HPI_ERROR_OBJ_ALREADY_OPEN) 117462306a36Sopenharmony_ci return -EBUSY; 117562306a36Sopenharmony_ci if (err) 117662306a36Sopenharmony_ci return -EIO; 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci timer_setup(&dpcm->timer, snd_card_asihpi_timer_function, 0); 117962306a36Sopenharmony_ci dpcm->substream = substream; 118062306a36Sopenharmony_ci runtime->private_data = dpcm; 118162306a36Sopenharmony_ci runtime->private_free = snd_card_asihpi_runtime_free; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci memset(&snd_card_asihpi_capture, 0, sizeof(snd_card_asihpi_capture)); 118462306a36Sopenharmony_ci if (!card->hpi->interrupt_mode) { 118562306a36Sopenharmony_ci snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX; 118662306a36Sopenharmony_ci snd_card_asihpi_capture.period_bytes_min = PERIOD_BYTES_MIN; 118762306a36Sopenharmony_ci snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; 118862306a36Sopenharmony_ci snd_card_asihpi_capture.periods_min = PERIODS_MIN; 118962306a36Sopenharmony_ci snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN; 119062306a36Sopenharmony_ci } else { 119162306a36Sopenharmony_ci size_t pbmin = card->update_interval_frames * 119262306a36Sopenharmony_ci card->out_max_chans; 119362306a36Sopenharmony_ci snd_card_asihpi_capture.buffer_bytes_max = BUFFER_BYTES_MAX; 119462306a36Sopenharmony_ci snd_card_asihpi_capture.period_bytes_min = pbmin; 119562306a36Sopenharmony_ci snd_card_asihpi_capture.period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN; 119662306a36Sopenharmony_ci snd_card_asihpi_capture.periods_min = PERIODS_MIN; 119762306a36Sopenharmony_ci snd_card_asihpi_capture.periods_max = BUFFER_BYTES_MAX / pbmin; 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci /* snd_card_asihpi_capture.fifo_size = 0; */ 120062306a36Sopenharmony_ci snd_card_asihpi_capture.channels_max = card->in_max_chans; 120162306a36Sopenharmony_ci snd_card_asihpi_capture.channels_min = card->in_min_chans; 120262306a36Sopenharmony_ci snd_card_asihpi_capture.formats = 120362306a36Sopenharmony_ci snd_card_asihpi_capture_formats(card, dpcm->h_stream); 120462306a36Sopenharmony_ci snd_card_asihpi_pcm_samplerates(card, &snd_card_asihpi_capture); 120562306a36Sopenharmony_ci snd_card_asihpi_capture.info = SNDRV_PCM_INFO_INTERLEAVED | 120662306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 120762306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if (card->support_grouping) 121062306a36Sopenharmony_ci snd_card_asihpi_capture.info |= SNDRV_PCM_INFO_SYNC_START; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci runtime->hw = snd_card_asihpi_capture; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci if (card->can_dma) 121562306a36Sopenharmony_ci err = snd_pcm_hw_constraint_pow2(runtime, 0, 121662306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_BUFFER_BYTES); 121762306a36Sopenharmony_ci if (err < 0) 121862306a36Sopenharmony_ci return err; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 122162306a36Sopenharmony_ci card->update_interval_frames); 122262306a36Sopenharmony_ci snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 122362306a36Sopenharmony_ci card->update_interval_frames, UINT_MAX); 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci snd_pcm_set_sync(substream); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci return 0; 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream) 123162306a36Sopenharmony_ci{ 123262306a36Sopenharmony_ci struct snd_card_asihpi_pcm *dpcm = substream->runtime->private_data; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci hpi_handle_error(hpi_instream_close(dpcm->h_stream)); 123562306a36Sopenharmony_ci return 0; 123662306a36Sopenharmony_ci} 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = { 123962306a36Sopenharmony_ci .open = snd_card_asihpi_capture_open, 124062306a36Sopenharmony_ci .close = snd_card_asihpi_capture_close, 124162306a36Sopenharmony_ci .hw_params = snd_card_asihpi_pcm_hw_params, 124262306a36Sopenharmony_ci .hw_free = snd_card_asihpi_hw_free, 124362306a36Sopenharmony_ci .prepare = snd_card_asihpi_capture_prepare, 124462306a36Sopenharmony_ci .trigger = snd_card_asihpi_trigger, 124562306a36Sopenharmony_ci .pointer = snd_card_asihpi_capture_pointer, 124662306a36Sopenharmony_ci}; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_cistatic int snd_card_asihpi_pcm_new(struct snd_card_asihpi *asihpi, int device) 124962306a36Sopenharmony_ci{ 125062306a36Sopenharmony_ci struct snd_pcm *pcm; 125162306a36Sopenharmony_ci int err; 125262306a36Sopenharmony_ci u16 num_instreams, num_outstreams, x16; 125362306a36Sopenharmony_ci u32 x32; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci err = hpi_adapter_get_info(asihpi->hpi->adapter->index, 125662306a36Sopenharmony_ci &num_outstreams, &num_instreams, 125762306a36Sopenharmony_ci &x16, &x32, &x16); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci err = snd_pcm_new(asihpi->card, "Asihpi PCM", device, 126062306a36Sopenharmony_ci num_outstreams, num_instreams, &pcm); 126162306a36Sopenharmony_ci if (err < 0) 126262306a36Sopenharmony_ci return err; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci /* pointer to ops struct is stored, dont change ops afterwards! */ 126562306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, 126662306a36Sopenharmony_ci &snd_card_asihpi_playback_mmap_ops); 126762306a36Sopenharmony_ci snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, 126862306a36Sopenharmony_ci &snd_card_asihpi_capture_mmap_ops); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci pcm->private_data = asihpi; 127162306a36Sopenharmony_ci pcm->info_flags = 0; 127262306a36Sopenharmony_ci strcpy(pcm->name, "Asihpi PCM"); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci /*? do we want to emulate MMAP for non-BBM cards? 127562306a36Sopenharmony_ci Jack doesn't work with ALSAs MMAP emulation - WHY NOT? */ 127662306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, 127762306a36Sopenharmony_ci &asihpi->pci->dev, 127862306a36Sopenharmony_ci 64*1024, BUFFER_BYTES_MAX); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci return 0; 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci/***************************** MIXER CONTROLS ****************/ 128462306a36Sopenharmony_cistruct hpi_control { 128562306a36Sopenharmony_ci u32 h_control; 128662306a36Sopenharmony_ci u16 control_type; 128762306a36Sopenharmony_ci u16 src_node_type; 128862306a36Sopenharmony_ci u16 src_node_index; 128962306a36Sopenharmony_ci u16 dst_node_type; 129062306a36Sopenharmony_ci u16 dst_node_index; 129162306a36Sopenharmony_ci u16 band; 129262306a36Sopenharmony_ci char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* copied to snd_ctl_elem_id.name[44]; */ 129362306a36Sopenharmony_ci}; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_cistatic const char * const asihpi_tuner_band_names[] = { 129662306a36Sopenharmony_ci "invalid", 129762306a36Sopenharmony_ci "AM", 129862306a36Sopenharmony_ci "FM mono", 129962306a36Sopenharmony_ci "TV NTSC-M", 130062306a36Sopenharmony_ci "FM stereo", 130162306a36Sopenharmony_ci "AUX", 130262306a36Sopenharmony_ci "TV PAL BG", 130362306a36Sopenharmony_ci "TV PAL I", 130462306a36Sopenharmony_ci "TV PAL DK", 130562306a36Sopenharmony_ci "TV SECAM", 130662306a36Sopenharmony_ci "TV DAB", 130762306a36Sopenharmony_ci}; 130862306a36Sopenharmony_ci/* Number of strings must match the enumerations for HPI_TUNER_BAND in hpi.h */ 130962306a36Sopenharmony_cicompile_time_assert( 131062306a36Sopenharmony_ci (ARRAY_SIZE(asihpi_tuner_band_names) == 131162306a36Sopenharmony_ci (HPI_TUNER_BAND_LAST+1)), 131262306a36Sopenharmony_ci assert_tuner_band_names_size); 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_cistatic const char * const asihpi_src_names[] = { 131562306a36Sopenharmony_ci "no source", 131662306a36Sopenharmony_ci "PCM", 131762306a36Sopenharmony_ci "Line", 131862306a36Sopenharmony_ci "Digital", 131962306a36Sopenharmony_ci "Tuner", 132062306a36Sopenharmony_ci "RF", 132162306a36Sopenharmony_ci "Clock", 132262306a36Sopenharmony_ci "Bitstream", 132362306a36Sopenharmony_ci "Mic", 132462306a36Sopenharmony_ci "Net", 132562306a36Sopenharmony_ci "Analog", 132662306a36Sopenharmony_ci "Adapter", 132762306a36Sopenharmony_ci "RTP", 132862306a36Sopenharmony_ci "Internal", 132962306a36Sopenharmony_ci "AVB", 133062306a36Sopenharmony_ci "BLU-Link" 133162306a36Sopenharmony_ci}; 133262306a36Sopenharmony_ci/* Number of strings must match the enumerations for HPI_SOURCENODES in hpi.h */ 133362306a36Sopenharmony_cicompile_time_assert( 133462306a36Sopenharmony_ci (ARRAY_SIZE(asihpi_src_names) == 133562306a36Sopenharmony_ci (HPI_SOURCENODE_LAST_INDEX-HPI_SOURCENODE_NONE+1)), 133662306a36Sopenharmony_ci assert_src_names_size); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_cistatic const char * const asihpi_dst_names[] = { 133962306a36Sopenharmony_ci "no destination", 134062306a36Sopenharmony_ci "PCM", 134162306a36Sopenharmony_ci "Line", 134262306a36Sopenharmony_ci "Digital", 134362306a36Sopenharmony_ci "RF", 134462306a36Sopenharmony_ci "Speaker", 134562306a36Sopenharmony_ci "Net", 134662306a36Sopenharmony_ci "Analog", 134762306a36Sopenharmony_ci "RTP", 134862306a36Sopenharmony_ci "AVB", 134962306a36Sopenharmony_ci "Internal", 135062306a36Sopenharmony_ci "BLU-Link" 135162306a36Sopenharmony_ci}; 135262306a36Sopenharmony_ci/* Number of strings must match the enumerations for HPI_DESTNODES in hpi.h */ 135362306a36Sopenharmony_cicompile_time_assert( 135462306a36Sopenharmony_ci (ARRAY_SIZE(asihpi_dst_names) == 135562306a36Sopenharmony_ci (HPI_DESTNODE_LAST_INDEX-HPI_DESTNODE_NONE+1)), 135662306a36Sopenharmony_ci assert_dst_names_size); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic inline int ctl_add(struct snd_card *card, struct snd_kcontrol_new *ctl, 135962306a36Sopenharmony_ci struct snd_card_asihpi *asihpi) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci int err; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(ctl, asihpi)); 136462306a36Sopenharmony_ci if (err < 0) 136562306a36Sopenharmony_ci return err; 136662306a36Sopenharmony_ci else if (mixer_dump) 136762306a36Sopenharmony_ci dev_info(&asihpi->pci->dev, "added %s(%d)\n", ctl->name, ctl->index); 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci return 0; 137062306a36Sopenharmony_ci} 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci/* Convert HPI control name and location into ALSA control name */ 137362306a36Sopenharmony_cistatic void asihpi_ctl_init(struct snd_kcontrol_new *snd_control, 137462306a36Sopenharmony_ci struct hpi_control *hpi_ctl, 137562306a36Sopenharmony_ci char *name) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci char *dir; 137862306a36Sopenharmony_ci memset(snd_control, 0, sizeof(*snd_control)); 137962306a36Sopenharmony_ci snd_control->name = hpi_ctl->name; 138062306a36Sopenharmony_ci snd_control->private_value = hpi_ctl->h_control; 138162306a36Sopenharmony_ci snd_control->iface = SNDRV_CTL_ELEM_IFACE_MIXER; 138262306a36Sopenharmony_ci snd_control->index = 0; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci if (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE == HPI_SOURCENODE_CLOCK_SOURCE) 138562306a36Sopenharmony_ci dir = ""; /* clock is neither capture nor playback */ 138662306a36Sopenharmony_ci else if (hpi_ctl->dst_node_type + HPI_DESTNODE_NONE == HPI_DESTNODE_ISTREAM) 138762306a36Sopenharmony_ci dir = "Capture "; /* On or towards a PCM capture destination*/ 138862306a36Sopenharmony_ci else if ((hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) && 138962306a36Sopenharmony_ci (!hpi_ctl->dst_node_type)) 139062306a36Sopenharmony_ci dir = "Capture "; /* On a source node that is not PCM playback */ 139162306a36Sopenharmony_ci else if (hpi_ctl->src_node_type && 139262306a36Sopenharmony_ci (hpi_ctl->src_node_type + HPI_SOURCENODE_NONE != HPI_SOURCENODE_OSTREAM) && 139362306a36Sopenharmony_ci (hpi_ctl->dst_node_type)) 139462306a36Sopenharmony_ci dir = "Monitor Playback "; /* Between an input and an output */ 139562306a36Sopenharmony_ci else 139662306a36Sopenharmony_ci dir = "Playback "; /* PCM Playback source, or output node */ 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci if (hpi_ctl->src_node_type && hpi_ctl->dst_node_type) 139962306a36Sopenharmony_ci sprintf(hpi_ctl->name, "%s %d %s %d %s%s", 140062306a36Sopenharmony_ci asihpi_src_names[hpi_ctl->src_node_type], 140162306a36Sopenharmony_ci hpi_ctl->src_node_index, 140262306a36Sopenharmony_ci asihpi_dst_names[hpi_ctl->dst_node_type], 140362306a36Sopenharmony_ci hpi_ctl->dst_node_index, 140462306a36Sopenharmony_ci dir, name); 140562306a36Sopenharmony_ci else if (hpi_ctl->dst_node_type) { 140662306a36Sopenharmony_ci sprintf(hpi_ctl->name, "%s %d %s%s", 140762306a36Sopenharmony_ci asihpi_dst_names[hpi_ctl->dst_node_type], 140862306a36Sopenharmony_ci hpi_ctl->dst_node_index, 140962306a36Sopenharmony_ci dir, name); 141062306a36Sopenharmony_ci } else { 141162306a36Sopenharmony_ci sprintf(hpi_ctl->name, "%s %d %s%s", 141262306a36Sopenharmony_ci asihpi_src_names[hpi_ctl->src_node_type], 141362306a36Sopenharmony_ci hpi_ctl->src_node_index, 141462306a36Sopenharmony_ci dir, name); 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci /* printk(KERN_INFO "Adding %s %d to %d ", hpi_ctl->name, 141762306a36Sopenharmony_ci hpi_ctl->wSrcNodeType, hpi_ctl->wDstNodeType); */ 141862306a36Sopenharmony_ci} 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci/*------------------------------------------------------------ 142162306a36Sopenharmony_ci Volume controls 142262306a36Sopenharmony_ci ------------------------------------------------------------*/ 142362306a36Sopenharmony_ci#define VOL_STEP_mB 1 142462306a36Sopenharmony_cistatic int snd_asihpi_volume_info(struct snd_kcontrol *kcontrol, 142562306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 142662306a36Sopenharmony_ci{ 142762306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 142862306a36Sopenharmony_ci u32 count; 142962306a36Sopenharmony_ci u16 err; 143062306a36Sopenharmony_ci /* native gains are in millibels */ 143162306a36Sopenharmony_ci short min_gain_mB; 143262306a36Sopenharmony_ci short max_gain_mB; 143362306a36Sopenharmony_ci short step_gain_mB; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci err = hpi_volume_query_range(h_control, 143662306a36Sopenharmony_ci &min_gain_mB, &max_gain_mB, &step_gain_mB); 143762306a36Sopenharmony_ci if (err) { 143862306a36Sopenharmony_ci max_gain_mB = 0; 143962306a36Sopenharmony_ci min_gain_mB = -10000; 144062306a36Sopenharmony_ci step_gain_mB = VOL_STEP_mB; 144162306a36Sopenharmony_ci } 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci err = hpi_meter_query_channels(h_control, &count); 144462306a36Sopenharmony_ci if (err) 144562306a36Sopenharmony_ci count = HPI_MAX_CHANNELS; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 144862306a36Sopenharmony_ci uinfo->count = count; 144962306a36Sopenharmony_ci uinfo->value.integer.min = min_gain_mB / VOL_STEP_mB; 145062306a36Sopenharmony_ci uinfo->value.integer.max = max_gain_mB / VOL_STEP_mB; 145162306a36Sopenharmony_ci uinfo->value.integer.step = step_gain_mB / VOL_STEP_mB; 145262306a36Sopenharmony_ci return 0; 145362306a36Sopenharmony_ci} 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_cistatic int snd_asihpi_volume_get(struct snd_kcontrol *kcontrol, 145662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 145762306a36Sopenharmony_ci{ 145862306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 145962306a36Sopenharmony_ci short an_gain_mB[HPI_MAX_CHANNELS]; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci hpi_handle_error(hpi_volume_get_gain(h_control, an_gain_mB)); 146262306a36Sopenharmony_ci ucontrol->value.integer.value[0] = an_gain_mB[0] / VOL_STEP_mB; 146362306a36Sopenharmony_ci ucontrol->value.integer.value[1] = an_gain_mB[1] / VOL_STEP_mB; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci return 0; 146662306a36Sopenharmony_ci} 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_cistatic int snd_asihpi_volume_put(struct snd_kcontrol *kcontrol, 146962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 147262306a36Sopenharmony_ci short an_gain_mB[HPI_MAX_CHANNELS]; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci an_gain_mB[0] = 147562306a36Sopenharmony_ci (ucontrol->value.integer.value[0]) * VOL_STEP_mB; 147662306a36Sopenharmony_ci an_gain_mB[1] = 147762306a36Sopenharmony_ci (ucontrol->value.integer.value[1]) * VOL_STEP_mB; 147862306a36Sopenharmony_ci /* change = asihpi->mixer_volume[addr][0] != left || 147962306a36Sopenharmony_ci asihpi->mixer_volume[addr][1] != right; 148062306a36Sopenharmony_ci */ 148162306a36Sopenharmony_ci hpi_handle_error(hpi_volume_set_gain(h_control, an_gain_mB)); 148262306a36Sopenharmony_ci return 1; 148362306a36Sopenharmony_ci} 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_100, -10000, VOL_STEP_mB, 0); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci#define snd_asihpi_volume_mute_info snd_ctl_boolean_mono_info 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_cistatic int snd_asihpi_volume_mute_get(struct snd_kcontrol *kcontrol, 149062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 149162306a36Sopenharmony_ci{ 149262306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 149362306a36Sopenharmony_ci u32 mute; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci hpi_handle_error(hpi_volume_get_mute(h_control, &mute)); 149662306a36Sopenharmony_ci ucontrol->value.integer.value[0] = mute ? 0 : 1; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci return 0; 149962306a36Sopenharmony_ci} 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_cistatic int snd_asihpi_volume_mute_put(struct snd_kcontrol *kcontrol, 150262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 150362306a36Sopenharmony_ci{ 150462306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 150562306a36Sopenharmony_ci /* HPI currently only supports all or none muting of multichannel volume 150662306a36Sopenharmony_ci ALSA Switch element has opposite sense to HPI mute: on==unmuted, off=muted 150762306a36Sopenharmony_ci */ 150862306a36Sopenharmony_ci int mute = ucontrol->value.integer.value[0] ? 0 : HPI_BITMASK_ALL_CHANNELS; 150962306a36Sopenharmony_ci hpi_handle_error(hpi_volume_set_mute(h_control, mute)); 151062306a36Sopenharmony_ci return 1; 151162306a36Sopenharmony_ci} 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_cistatic int snd_asihpi_volume_add(struct snd_card_asihpi *asihpi, 151462306a36Sopenharmony_ci struct hpi_control *hpi_ctl) 151562306a36Sopenharmony_ci{ 151662306a36Sopenharmony_ci struct snd_card *card = asihpi->card; 151762306a36Sopenharmony_ci struct snd_kcontrol_new snd_control; 151862306a36Sopenharmony_ci int err; 151962306a36Sopenharmony_ci u32 mute; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Volume"); 152262306a36Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 152362306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ; 152462306a36Sopenharmony_ci snd_control.info = snd_asihpi_volume_info; 152562306a36Sopenharmony_ci snd_control.get = snd_asihpi_volume_get; 152662306a36Sopenharmony_ci snd_control.put = snd_asihpi_volume_put; 152762306a36Sopenharmony_ci snd_control.tlv.p = db_scale_100; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci err = ctl_add(card, &snd_control, asihpi); 153062306a36Sopenharmony_ci if (err) 153162306a36Sopenharmony_ci return err; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci if (hpi_volume_get_mute(hpi_ctl->h_control, &mute) == 0) { 153462306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Switch"); 153562306a36Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 153662306a36Sopenharmony_ci snd_control.info = snd_asihpi_volume_mute_info; 153762306a36Sopenharmony_ci snd_control.get = snd_asihpi_volume_mute_get; 153862306a36Sopenharmony_ci snd_control.put = snd_asihpi_volume_mute_put; 153962306a36Sopenharmony_ci err = ctl_add(card, &snd_control, asihpi); 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci return err; 154262306a36Sopenharmony_ci} 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci/*------------------------------------------------------------ 154562306a36Sopenharmony_ci Level controls 154662306a36Sopenharmony_ci ------------------------------------------------------------*/ 154762306a36Sopenharmony_cistatic int snd_asihpi_level_info(struct snd_kcontrol *kcontrol, 154862306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 154962306a36Sopenharmony_ci{ 155062306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 155162306a36Sopenharmony_ci u16 err; 155262306a36Sopenharmony_ci short min_gain_mB; 155362306a36Sopenharmony_ci short max_gain_mB; 155462306a36Sopenharmony_ci short step_gain_mB; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci err = 155762306a36Sopenharmony_ci hpi_level_query_range(h_control, &min_gain_mB, 155862306a36Sopenharmony_ci &max_gain_mB, &step_gain_mB); 155962306a36Sopenharmony_ci if (err) { 156062306a36Sopenharmony_ci max_gain_mB = 2400; 156162306a36Sopenharmony_ci min_gain_mB = -1000; 156262306a36Sopenharmony_ci step_gain_mB = 100; 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 156662306a36Sopenharmony_ci uinfo->count = 2; 156762306a36Sopenharmony_ci uinfo->value.integer.min = min_gain_mB / HPI_UNITS_PER_dB; 156862306a36Sopenharmony_ci uinfo->value.integer.max = max_gain_mB / HPI_UNITS_PER_dB; 156962306a36Sopenharmony_ci uinfo->value.integer.step = step_gain_mB / HPI_UNITS_PER_dB; 157062306a36Sopenharmony_ci return 0; 157162306a36Sopenharmony_ci} 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_cistatic int snd_asihpi_level_get(struct snd_kcontrol *kcontrol, 157462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 157562306a36Sopenharmony_ci{ 157662306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 157762306a36Sopenharmony_ci short an_gain_mB[HPI_MAX_CHANNELS]; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci hpi_handle_error(hpi_level_get_gain(h_control, an_gain_mB)); 158062306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 158162306a36Sopenharmony_ci an_gain_mB[0] / HPI_UNITS_PER_dB; 158262306a36Sopenharmony_ci ucontrol->value.integer.value[1] = 158362306a36Sopenharmony_ci an_gain_mB[1] / HPI_UNITS_PER_dB; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci return 0; 158662306a36Sopenharmony_ci} 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_cistatic int snd_asihpi_level_put(struct snd_kcontrol *kcontrol, 158962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 159062306a36Sopenharmony_ci{ 159162306a36Sopenharmony_ci int change; 159262306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 159362306a36Sopenharmony_ci short an_gain_mB[HPI_MAX_CHANNELS]; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci an_gain_mB[0] = 159662306a36Sopenharmony_ci (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB; 159762306a36Sopenharmony_ci an_gain_mB[1] = 159862306a36Sopenharmony_ci (ucontrol->value.integer.value[1]) * HPI_UNITS_PER_dB; 159962306a36Sopenharmony_ci /* change = asihpi->mixer_level[addr][0] != left || 160062306a36Sopenharmony_ci asihpi->mixer_level[addr][1] != right; 160162306a36Sopenharmony_ci */ 160262306a36Sopenharmony_ci change = 1; 160362306a36Sopenharmony_ci hpi_handle_error(hpi_level_set_gain(h_control, an_gain_mB)); 160462306a36Sopenharmony_ci return change; 160562306a36Sopenharmony_ci} 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(db_scale_level, -1000, 100, 0); 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_cistatic int snd_asihpi_level_add(struct snd_card_asihpi *asihpi, 161062306a36Sopenharmony_ci struct hpi_control *hpi_ctl) 161162306a36Sopenharmony_ci{ 161262306a36Sopenharmony_ci struct snd_card *card = asihpi->card; 161362306a36Sopenharmony_ci struct snd_kcontrol_new snd_control; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci /* can't use 'volume' cos some nodes have volume as well */ 161662306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Level"); 161762306a36Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | 161862306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_TLV_READ; 161962306a36Sopenharmony_ci snd_control.info = snd_asihpi_level_info; 162062306a36Sopenharmony_ci snd_control.get = snd_asihpi_level_get; 162162306a36Sopenharmony_ci snd_control.put = snd_asihpi_level_put; 162262306a36Sopenharmony_ci snd_control.tlv.p = db_scale_level; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 162562306a36Sopenharmony_ci} 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci/*------------------------------------------------------------ 162862306a36Sopenharmony_ci AESEBU controls 162962306a36Sopenharmony_ci ------------------------------------------------------------*/ 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci/* AESEBU format */ 163262306a36Sopenharmony_cistatic const char * const asihpi_aesebu_format_names[] = { 163362306a36Sopenharmony_ci "N/A", "S/PDIF", "AES/EBU" }; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_cistatic int snd_asihpi_aesebu_format_info(struct snd_kcontrol *kcontrol, 163662306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 163762306a36Sopenharmony_ci{ 163862306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, 3, asihpi_aesebu_format_names); 163962306a36Sopenharmony_ci} 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_cistatic int snd_asihpi_aesebu_format_get(struct snd_kcontrol *kcontrol, 164262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol, 164362306a36Sopenharmony_ci u16 (*func)(u32, u16 *)) 164462306a36Sopenharmony_ci{ 164562306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 164662306a36Sopenharmony_ci u16 source, err; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci err = func(h_control, &source); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci /* default to N/A */ 165162306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = 0; 165262306a36Sopenharmony_ci /* return success but set the control to N/A */ 165362306a36Sopenharmony_ci if (err) 165462306a36Sopenharmony_ci return 0; 165562306a36Sopenharmony_ci if (source == HPI_AESEBU_FORMAT_SPDIF) 165662306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = 1; 165762306a36Sopenharmony_ci if (source == HPI_AESEBU_FORMAT_AESEBU) 165862306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = 2; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci return 0; 166162306a36Sopenharmony_ci} 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_cistatic int snd_asihpi_aesebu_format_put(struct snd_kcontrol *kcontrol, 166462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol, 166562306a36Sopenharmony_ci u16 (*func)(u32, u16)) 166662306a36Sopenharmony_ci{ 166762306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci /* default to S/PDIF */ 167062306a36Sopenharmony_ci u16 source = HPI_AESEBU_FORMAT_SPDIF; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci if (ucontrol->value.enumerated.item[0] == 1) 167362306a36Sopenharmony_ci source = HPI_AESEBU_FORMAT_SPDIF; 167462306a36Sopenharmony_ci if (ucontrol->value.enumerated.item[0] == 2) 167562306a36Sopenharmony_ci source = HPI_AESEBU_FORMAT_AESEBU; 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci if (func(h_control, source) != 0) 167862306a36Sopenharmony_ci return -EINVAL; 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci return 1; 168162306a36Sopenharmony_ci} 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_cistatic int snd_asihpi_aesebu_rx_format_get(struct snd_kcontrol *kcontrol, 168462306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) { 168562306a36Sopenharmony_ci return snd_asihpi_aesebu_format_get(kcontrol, ucontrol, 168662306a36Sopenharmony_ci hpi_aesebu_receiver_get_format); 168762306a36Sopenharmony_ci} 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_cistatic int snd_asihpi_aesebu_rx_format_put(struct snd_kcontrol *kcontrol, 169062306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) { 169162306a36Sopenharmony_ci return snd_asihpi_aesebu_format_put(kcontrol, ucontrol, 169262306a36Sopenharmony_ci hpi_aesebu_receiver_set_format); 169362306a36Sopenharmony_ci} 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_cistatic int snd_asihpi_aesebu_rxstatus_info(struct snd_kcontrol *kcontrol, 169662306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 169762306a36Sopenharmony_ci{ 169862306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 169962306a36Sopenharmony_ci uinfo->count = 1; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci uinfo->value.integer.min = 0; 170262306a36Sopenharmony_ci uinfo->value.integer.max = 0X1F; 170362306a36Sopenharmony_ci uinfo->value.integer.step = 1; 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci return 0; 170662306a36Sopenharmony_ci} 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_cistatic int snd_asihpi_aesebu_rxstatus_get(struct snd_kcontrol *kcontrol, 170962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) { 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 171262306a36Sopenharmony_ci u16 status; 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci hpi_handle_error(hpi_aesebu_receiver_get_error_status( 171562306a36Sopenharmony_ci h_control, &status)); 171662306a36Sopenharmony_ci ucontrol->value.integer.value[0] = status; 171762306a36Sopenharmony_ci return 0; 171862306a36Sopenharmony_ci} 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_cistatic int snd_asihpi_aesebu_rx_add(struct snd_card_asihpi *asihpi, 172162306a36Sopenharmony_ci struct hpi_control *hpi_ctl) 172262306a36Sopenharmony_ci{ 172362306a36Sopenharmony_ci struct snd_card *card = asihpi->card; 172462306a36Sopenharmony_ci struct snd_kcontrol_new snd_control; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Format"); 172762306a36Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 172862306a36Sopenharmony_ci snd_control.info = snd_asihpi_aesebu_format_info; 172962306a36Sopenharmony_ci snd_control.get = snd_asihpi_aesebu_rx_format_get; 173062306a36Sopenharmony_ci snd_control.put = snd_asihpi_aesebu_rx_format_put; 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci if (ctl_add(card, &snd_control, asihpi) < 0) 173462306a36Sopenharmony_ci return -EINVAL; 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Status"); 173762306a36Sopenharmony_ci snd_control.access = 173862306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; 173962306a36Sopenharmony_ci snd_control.info = snd_asihpi_aesebu_rxstatus_info; 174062306a36Sopenharmony_ci snd_control.get = snd_asihpi_aesebu_rxstatus_get; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 174362306a36Sopenharmony_ci} 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_cistatic int snd_asihpi_aesebu_tx_format_get(struct snd_kcontrol *kcontrol, 174662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) { 174762306a36Sopenharmony_ci return snd_asihpi_aesebu_format_get(kcontrol, ucontrol, 174862306a36Sopenharmony_ci hpi_aesebu_transmitter_get_format); 174962306a36Sopenharmony_ci} 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_cistatic int snd_asihpi_aesebu_tx_format_put(struct snd_kcontrol *kcontrol, 175262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) { 175362306a36Sopenharmony_ci return snd_asihpi_aesebu_format_put(kcontrol, ucontrol, 175462306a36Sopenharmony_ci hpi_aesebu_transmitter_set_format); 175562306a36Sopenharmony_ci} 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_cistatic int snd_asihpi_aesebu_tx_add(struct snd_card_asihpi *asihpi, 175962306a36Sopenharmony_ci struct hpi_control *hpi_ctl) 176062306a36Sopenharmony_ci{ 176162306a36Sopenharmony_ci struct snd_card *card = asihpi->card; 176262306a36Sopenharmony_ci struct snd_kcontrol_new snd_control; 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Format"); 176562306a36Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 176662306a36Sopenharmony_ci snd_control.info = snd_asihpi_aesebu_format_info; 176762306a36Sopenharmony_ci snd_control.get = snd_asihpi_aesebu_tx_format_get; 176862306a36Sopenharmony_ci snd_control.put = snd_asihpi_aesebu_tx_format_put; 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 177162306a36Sopenharmony_ci} 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci/*------------------------------------------------------------ 177462306a36Sopenharmony_ci Tuner controls 177562306a36Sopenharmony_ci ------------------------------------------------------------*/ 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci/* Gain */ 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_cistatic int snd_asihpi_tuner_gain_info(struct snd_kcontrol *kcontrol, 178062306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 178162306a36Sopenharmony_ci{ 178262306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 178362306a36Sopenharmony_ci u16 err; 178462306a36Sopenharmony_ci short idx; 178562306a36Sopenharmony_ci u16 gain_range[3]; 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci for (idx = 0; idx < 3; idx++) { 178862306a36Sopenharmony_ci err = hpi_tuner_query_gain(h_control, 178962306a36Sopenharmony_ci idx, &gain_range[idx]); 179062306a36Sopenharmony_ci if (err != 0) 179162306a36Sopenharmony_ci return err; 179262306a36Sopenharmony_ci } 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 179562306a36Sopenharmony_ci uinfo->count = 1; 179662306a36Sopenharmony_ci uinfo->value.integer.min = ((int)gain_range[0]) / HPI_UNITS_PER_dB; 179762306a36Sopenharmony_ci uinfo->value.integer.max = ((int)gain_range[1]) / HPI_UNITS_PER_dB; 179862306a36Sopenharmony_ci uinfo->value.integer.step = ((int) gain_range[2]) / HPI_UNITS_PER_dB; 179962306a36Sopenharmony_ci return 0; 180062306a36Sopenharmony_ci} 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_cistatic int snd_asihpi_tuner_gain_get(struct snd_kcontrol *kcontrol, 180362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 180462306a36Sopenharmony_ci{ 180562306a36Sopenharmony_ci /* 180662306a36Sopenharmony_ci struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); 180762306a36Sopenharmony_ci */ 180862306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 180962306a36Sopenharmony_ci short gain; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci hpi_handle_error(hpi_tuner_get_gain(h_control, &gain)); 181262306a36Sopenharmony_ci ucontrol->value.integer.value[0] = gain / HPI_UNITS_PER_dB; 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci return 0; 181562306a36Sopenharmony_ci} 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_cistatic int snd_asihpi_tuner_gain_put(struct snd_kcontrol *kcontrol, 181862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 181962306a36Sopenharmony_ci{ 182062306a36Sopenharmony_ci /* 182162306a36Sopenharmony_ci struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); 182262306a36Sopenharmony_ci */ 182362306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 182462306a36Sopenharmony_ci short gain; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci gain = (ucontrol->value.integer.value[0]) * HPI_UNITS_PER_dB; 182762306a36Sopenharmony_ci hpi_handle_error(hpi_tuner_set_gain(h_control, gain)); 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci return 1; 183062306a36Sopenharmony_ci} 183162306a36Sopenharmony_ci 183262306a36Sopenharmony_ci/* Band */ 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_cistatic int asihpi_tuner_band_query(struct snd_kcontrol *kcontrol, 183562306a36Sopenharmony_ci u16 *band_list, u32 len) { 183662306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 183762306a36Sopenharmony_ci u16 err = 0; 183862306a36Sopenharmony_ci u32 i; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci for (i = 0; i < len; i++) { 184162306a36Sopenharmony_ci err = hpi_tuner_query_band( 184262306a36Sopenharmony_ci h_control, i, &band_list[i]); 184362306a36Sopenharmony_ci if (err != 0) 184462306a36Sopenharmony_ci break; 184562306a36Sopenharmony_ci } 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci if (err && (err != HPI_ERROR_INVALID_OBJ_INDEX)) 184862306a36Sopenharmony_ci return -EIO; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci return i; 185162306a36Sopenharmony_ci} 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_cistatic int snd_asihpi_tuner_band_info(struct snd_kcontrol *kcontrol, 185462306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 185562306a36Sopenharmony_ci{ 185662306a36Sopenharmony_ci u16 tuner_bands[HPI_TUNER_BAND_LAST]; 185762306a36Sopenharmony_ci int num_bands = 0; 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, 186062306a36Sopenharmony_ci HPI_TUNER_BAND_LAST); 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci if (num_bands < 0) 186362306a36Sopenharmony_ci return num_bands; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, num_bands, asihpi_tuner_band_names); 186662306a36Sopenharmony_ci} 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_cistatic int snd_asihpi_tuner_band_get(struct snd_kcontrol *kcontrol, 186962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 187062306a36Sopenharmony_ci{ 187162306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 187262306a36Sopenharmony_ci /* 187362306a36Sopenharmony_ci struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); 187462306a36Sopenharmony_ci */ 187562306a36Sopenharmony_ci u16 band, idx; 187662306a36Sopenharmony_ci u16 tuner_bands[HPI_TUNER_BAND_LAST]; 187762306a36Sopenharmony_ci __always_unused u32 num_bands; 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, 188062306a36Sopenharmony_ci HPI_TUNER_BAND_LAST); 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci hpi_handle_error(hpi_tuner_get_band(h_control, &band)); 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = -1; 188562306a36Sopenharmony_ci for (idx = 0; idx < HPI_TUNER_BAND_LAST; idx++) 188662306a36Sopenharmony_ci if (tuner_bands[idx] == band) { 188762306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = idx; 188862306a36Sopenharmony_ci break; 188962306a36Sopenharmony_ci } 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ci return 0; 189262306a36Sopenharmony_ci} 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_cistatic int snd_asihpi_tuner_band_put(struct snd_kcontrol *kcontrol, 189562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 189662306a36Sopenharmony_ci{ 189762306a36Sopenharmony_ci /* 189862306a36Sopenharmony_ci struct snd_card_asihpi *asihpi = snd_kcontrol_chip(kcontrol); 189962306a36Sopenharmony_ci */ 190062306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 190162306a36Sopenharmony_ci unsigned int idx; 190262306a36Sopenharmony_ci u16 band; 190362306a36Sopenharmony_ci u16 tuner_bands[HPI_TUNER_BAND_LAST]; 190462306a36Sopenharmony_ci __always_unused u32 num_bands; 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, 190762306a36Sopenharmony_ci HPI_TUNER_BAND_LAST); 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci idx = ucontrol->value.enumerated.item[0]; 191062306a36Sopenharmony_ci if (idx >= ARRAY_SIZE(tuner_bands)) 191162306a36Sopenharmony_ci idx = ARRAY_SIZE(tuner_bands) - 1; 191262306a36Sopenharmony_ci band = tuner_bands[idx]; 191362306a36Sopenharmony_ci hpi_handle_error(hpi_tuner_set_band(h_control, band)); 191462306a36Sopenharmony_ci 191562306a36Sopenharmony_ci return 1; 191662306a36Sopenharmony_ci} 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci/* Freq */ 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_cistatic int snd_asihpi_tuner_freq_info(struct snd_kcontrol *kcontrol, 192162306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 192262306a36Sopenharmony_ci{ 192362306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 192462306a36Sopenharmony_ci u16 err; 192562306a36Sopenharmony_ci u16 tuner_bands[HPI_TUNER_BAND_LAST]; 192662306a36Sopenharmony_ci u16 num_bands = 0, band_iter, idx; 192762306a36Sopenharmony_ci u32 freq_range[3], temp_freq_range[3]; 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci num_bands = asihpi_tuner_band_query(kcontrol, tuner_bands, 193062306a36Sopenharmony_ci HPI_TUNER_BAND_LAST); 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci freq_range[0] = INT_MAX; 193362306a36Sopenharmony_ci freq_range[1] = 0; 193462306a36Sopenharmony_ci freq_range[2] = INT_MAX; 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci for (band_iter = 0; band_iter < num_bands; band_iter++) { 193762306a36Sopenharmony_ci for (idx = 0; idx < 3; idx++) { 193862306a36Sopenharmony_ci err = hpi_tuner_query_frequency(h_control, 193962306a36Sopenharmony_ci idx, tuner_bands[band_iter], 194062306a36Sopenharmony_ci &temp_freq_range[idx]); 194162306a36Sopenharmony_ci if (err != 0) 194262306a36Sopenharmony_ci return err; 194362306a36Sopenharmony_ci } 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci /* skip band with bogus stepping */ 194662306a36Sopenharmony_ci if (temp_freq_range[2] <= 0) 194762306a36Sopenharmony_ci continue; 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_ci if (temp_freq_range[0] < freq_range[0]) 195062306a36Sopenharmony_ci freq_range[0] = temp_freq_range[0]; 195162306a36Sopenharmony_ci if (temp_freq_range[1] > freq_range[1]) 195262306a36Sopenharmony_ci freq_range[1] = temp_freq_range[1]; 195362306a36Sopenharmony_ci if (temp_freq_range[2] < freq_range[2]) 195462306a36Sopenharmony_ci freq_range[2] = temp_freq_range[2]; 195562306a36Sopenharmony_ci } 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 195862306a36Sopenharmony_ci uinfo->count = 1; 195962306a36Sopenharmony_ci uinfo->value.integer.min = ((int)freq_range[0]); 196062306a36Sopenharmony_ci uinfo->value.integer.max = ((int)freq_range[1]); 196162306a36Sopenharmony_ci uinfo->value.integer.step = ((int)freq_range[2]); 196262306a36Sopenharmony_ci return 0; 196362306a36Sopenharmony_ci} 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_cistatic int snd_asihpi_tuner_freq_get(struct snd_kcontrol *kcontrol, 196662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 196762306a36Sopenharmony_ci{ 196862306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 196962306a36Sopenharmony_ci u32 freq; 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci hpi_handle_error(hpi_tuner_get_frequency(h_control, &freq)); 197262306a36Sopenharmony_ci ucontrol->value.integer.value[0] = freq; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci return 0; 197562306a36Sopenharmony_ci} 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_cistatic int snd_asihpi_tuner_freq_put(struct snd_kcontrol *kcontrol, 197862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 197962306a36Sopenharmony_ci{ 198062306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 198162306a36Sopenharmony_ci u32 freq; 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci freq = ucontrol->value.integer.value[0]; 198462306a36Sopenharmony_ci hpi_handle_error(hpi_tuner_set_frequency(h_control, freq)); 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci return 1; 198762306a36Sopenharmony_ci} 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci/* Tuner control group initializer */ 199062306a36Sopenharmony_cistatic int snd_asihpi_tuner_add(struct snd_card_asihpi *asihpi, 199162306a36Sopenharmony_ci struct hpi_control *hpi_ctl) 199262306a36Sopenharmony_ci{ 199362306a36Sopenharmony_ci struct snd_card *card = asihpi->card; 199462306a36Sopenharmony_ci struct snd_kcontrol_new snd_control; 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci snd_control.private_value = hpi_ctl->h_control; 199762306a36Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci if (!hpi_tuner_get_gain(hpi_ctl->h_control, NULL)) { 200062306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Gain"); 200162306a36Sopenharmony_ci snd_control.info = snd_asihpi_tuner_gain_info; 200262306a36Sopenharmony_ci snd_control.get = snd_asihpi_tuner_gain_get; 200362306a36Sopenharmony_ci snd_control.put = snd_asihpi_tuner_gain_put; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci if (ctl_add(card, &snd_control, asihpi) < 0) 200662306a36Sopenharmony_ci return -EINVAL; 200762306a36Sopenharmony_ci } 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Band"); 201062306a36Sopenharmony_ci snd_control.info = snd_asihpi_tuner_band_info; 201162306a36Sopenharmony_ci snd_control.get = snd_asihpi_tuner_band_get; 201262306a36Sopenharmony_ci snd_control.put = snd_asihpi_tuner_band_put; 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci if (ctl_add(card, &snd_control, asihpi) < 0) 201562306a36Sopenharmony_ci return -EINVAL; 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Freq"); 201862306a36Sopenharmony_ci snd_control.info = snd_asihpi_tuner_freq_info; 201962306a36Sopenharmony_ci snd_control.get = snd_asihpi_tuner_freq_get; 202062306a36Sopenharmony_ci snd_control.put = snd_asihpi_tuner_freq_put; 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 202362306a36Sopenharmony_ci} 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci/*------------------------------------------------------------ 202662306a36Sopenharmony_ci Meter controls 202762306a36Sopenharmony_ci ------------------------------------------------------------*/ 202862306a36Sopenharmony_cistatic int snd_asihpi_meter_info(struct snd_kcontrol *kcontrol, 202962306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 203062306a36Sopenharmony_ci{ 203162306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 203262306a36Sopenharmony_ci u32 count; 203362306a36Sopenharmony_ci u16 err; 203462306a36Sopenharmony_ci err = hpi_meter_query_channels(h_control, &count); 203562306a36Sopenharmony_ci if (err) 203662306a36Sopenharmony_ci count = HPI_MAX_CHANNELS; 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 203962306a36Sopenharmony_ci uinfo->count = count; 204062306a36Sopenharmony_ci uinfo->value.integer.min = 0; 204162306a36Sopenharmony_ci uinfo->value.integer.max = 0x7FFFFFFF; 204262306a36Sopenharmony_ci return 0; 204362306a36Sopenharmony_ci} 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci/* linear values for 10dB steps */ 204662306a36Sopenharmony_cistatic const int log2lin[] = { 204762306a36Sopenharmony_ci 0x7FFFFFFF, /* 0dB */ 204862306a36Sopenharmony_ci 679093956, 204962306a36Sopenharmony_ci 214748365, 205062306a36Sopenharmony_ci 67909396, 205162306a36Sopenharmony_ci 21474837, 205262306a36Sopenharmony_ci 6790940, 205362306a36Sopenharmony_ci 2147484, /* -60dB */ 205462306a36Sopenharmony_ci 679094, 205562306a36Sopenharmony_ci 214748, /* -80 */ 205662306a36Sopenharmony_ci 67909, 205762306a36Sopenharmony_ci 21475, /* -100 */ 205862306a36Sopenharmony_ci 6791, 205962306a36Sopenharmony_ci 2147, 206062306a36Sopenharmony_ci 679, 206162306a36Sopenharmony_ci 214, 206262306a36Sopenharmony_ci 68, 206362306a36Sopenharmony_ci 21, 206462306a36Sopenharmony_ci 7, 206562306a36Sopenharmony_ci 2 206662306a36Sopenharmony_ci}; 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_cistatic int snd_asihpi_meter_get(struct snd_kcontrol *kcontrol, 206962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 207062306a36Sopenharmony_ci{ 207162306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 207262306a36Sopenharmony_ci short an_gain_mB[HPI_MAX_CHANNELS], i; 207362306a36Sopenharmony_ci u16 err; 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci err = hpi_meter_get_peak(h_control, an_gain_mB); 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci for (i = 0; i < HPI_MAX_CHANNELS; i++) { 207862306a36Sopenharmony_ci if (err) { 207962306a36Sopenharmony_ci ucontrol->value.integer.value[i] = 0; 208062306a36Sopenharmony_ci } else if (an_gain_mB[i] >= 0) { 208162306a36Sopenharmony_ci ucontrol->value.integer.value[i] = 208262306a36Sopenharmony_ci an_gain_mB[i] << 16; 208362306a36Sopenharmony_ci } else { 208462306a36Sopenharmony_ci /* -ve is log value in millibels < -60dB, 208562306a36Sopenharmony_ci * convert to (roughly!) linear, 208662306a36Sopenharmony_ci */ 208762306a36Sopenharmony_ci ucontrol->value.integer.value[i] = 208862306a36Sopenharmony_ci log2lin[an_gain_mB[i] / -1000]; 208962306a36Sopenharmony_ci } 209062306a36Sopenharmony_ci } 209162306a36Sopenharmony_ci return 0; 209262306a36Sopenharmony_ci} 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_cistatic int snd_asihpi_meter_add(struct snd_card_asihpi *asihpi, 209562306a36Sopenharmony_ci struct hpi_control *hpi_ctl, int subidx) 209662306a36Sopenharmony_ci{ 209762306a36Sopenharmony_ci struct snd_card *card = asihpi->card; 209862306a36Sopenharmony_ci struct snd_kcontrol_new snd_control; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Meter"); 210162306a36Sopenharmony_ci snd_control.access = 210262306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; 210362306a36Sopenharmony_ci snd_control.info = snd_asihpi_meter_info; 210462306a36Sopenharmony_ci snd_control.get = snd_asihpi_meter_get; 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci snd_control.index = subidx; 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 210962306a36Sopenharmony_ci} 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci/*------------------------------------------------------------ 211262306a36Sopenharmony_ci Multiplexer controls 211362306a36Sopenharmony_ci ------------------------------------------------------------*/ 211462306a36Sopenharmony_cistatic int snd_card_asihpi_mux_count_sources(struct snd_kcontrol *snd_control) 211562306a36Sopenharmony_ci{ 211662306a36Sopenharmony_ci u32 h_control = snd_control->private_value; 211762306a36Sopenharmony_ci struct hpi_control hpi_ctl; 211862306a36Sopenharmony_ci int s, err; 211962306a36Sopenharmony_ci for (s = 0; s < 32; s++) { 212062306a36Sopenharmony_ci err = hpi_multiplexer_query_source(h_control, s, 212162306a36Sopenharmony_ci &hpi_ctl. 212262306a36Sopenharmony_ci src_node_type, 212362306a36Sopenharmony_ci &hpi_ctl. 212462306a36Sopenharmony_ci src_node_index); 212562306a36Sopenharmony_ci if (err) 212662306a36Sopenharmony_ci break; 212762306a36Sopenharmony_ci } 212862306a36Sopenharmony_ci return s; 212962306a36Sopenharmony_ci} 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_cistatic int snd_asihpi_mux_info(struct snd_kcontrol *kcontrol, 213262306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 213362306a36Sopenharmony_ci{ 213462306a36Sopenharmony_ci u16 src_node_type, src_node_index; 213562306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 213862306a36Sopenharmony_ci uinfo->count = 1; 213962306a36Sopenharmony_ci uinfo->value.enumerated.items = 214062306a36Sopenharmony_ci snd_card_asihpi_mux_count_sources(kcontrol); 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 214362306a36Sopenharmony_ci uinfo->value.enumerated.item = 214462306a36Sopenharmony_ci uinfo->value.enumerated.items - 1; 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci hpi_multiplexer_query_source(h_control, 214762306a36Sopenharmony_ci uinfo->value.enumerated.item, 214862306a36Sopenharmony_ci &src_node_type, &src_node_index); 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci sprintf(uinfo->value.enumerated.name, "%s %d", 215162306a36Sopenharmony_ci asihpi_src_names[src_node_type - HPI_SOURCENODE_NONE], 215262306a36Sopenharmony_ci src_node_index); 215362306a36Sopenharmony_ci return 0; 215462306a36Sopenharmony_ci} 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_cistatic int snd_asihpi_mux_get(struct snd_kcontrol *kcontrol, 215762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 215862306a36Sopenharmony_ci{ 215962306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 216062306a36Sopenharmony_ci u16 source_type, source_index; 216162306a36Sopenharmony_ci u16 src_node_type, src_node_index; 216262306a36Sopenharmony_ci int s; 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci hpi_handle_error(hpi_multiplexer_get_source(h_control, 216562306a36Sopenharmony_ci &source_type, &source_index)); 216662306a36Sopenharmony_ci /* Should cache this search result! */ 216762306a36Sopenharmony_ci for (s = 0; s < 256; s++) { 216862306a36Sopenharmony_ci if (hpi_multiplexer_query_source(h_control, s, 216962306a36Sopenharmony_ci &src_node_type, &src_node_index)) 217062306a36Sopenharmony_ci break; 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci if ((source_type == src_node_type) 217362306a36Sopenharmony_ci && (source_index == src_node_index)) { 217462306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = s; 217562306a36Sopenharmony_ci return 0; 217662306a36Sopenharmony_ci } 217762306a36Sopenharmony_ci } 217862306a36Sopenharmony_ci snd_printd(KERN_WARNING 217962306a36Sopenharmony_ci "Control %x failed to match mux source %hu %hu\n", 218062306a36Sopenharmony_ci h_control, source_type, source_index); 218162306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = 0; 218262306a36Sopenharmony_ci return 0; 218362306a36Sopenharmony_ci} 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_cistatic int snd_asihpi_mux_put(struct snd_kcontrol *kcontrol, 218662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 218762306a36Sopenharmony_ci{ 218862306a36Sopenharmony_ci int change; 218962306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 219062306a36Sopenharmony_ci u16 source_type, source_index; 219162306a36Sopenharmony_ci u16 e; 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci change = 1; 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci e = hpi_multiplexer_query_source(h_control, 219662306a36Sopenharmony_ci ucontrol->value.enumerated.item[0], 219762306a36Sopenharmony_ci &source_type, &source_index); 219862306a36Sopenharmony_ci if (!e) 219962306a36Sopenharmony_ci hpi_handle_error( 220062306a36Sopenharmony_ci hpi_multiplexer_set_source(h_control, 220162306a36Sopenharmony_ci source_type, source_index)); 220262306a36Sopenharmony_ci return change; 220362306a36Sopenharmony_ci} 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_cistatic int snd_asihpi_mux_add(struct snd_card_asihpi *asihpi, 220762306a36Sopenharmony_ci struct hpi_control *hpi_ctl) 220862306a36Sopenharmony_ci{ 220962306a36Sopenharmony_ci struct snd_card *card = asihpi->card; 221062306a36Sopenharmony_ci struct snd_kcontrol_new snd_control; 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Route"); 221362306a36Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 221462306a36Sopenharmony_ci snd_control.info = snd_asihpi_mux_info; 221562306a36Sopenharmony_ci snd_control.get = snd_asihpi_mux_get; 221662306a36Sopenharmony_ci snd_control.put = snd_asihpi_mux_put; 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci} 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci/*------------------------------------------------------------ 222362306a36Sopenharmony_ci Channel mode controls 222462306a36Sopenharmony_ci ------------------------------------------------------------*/ 222562306a36Sopenharmony_cistatic int snd_asihpi_cmode_info(struct snd_kcontrol *kcontrol, 222662306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 222762306a36Sopenharmony_ci{ 222862306a36Sopenharmony_ci static const char * const mode_names[HPI_CHANNEL_MODE_LAST + 1] = { 222962306a36Sopenharmony_ci "invalid", 223062306a36Sopenharmony_ci "Normal", "Swap", 223162306a36Sopenharmony_ci "From Left", "From Right", 223262306a36Sopenharmony_ci "To Left", "To Right" 223362306a36Sopenharmony_ci }; 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 223662306a36Sopenharmony_ci u16 mode; 223762306a36Sopenharmony_ci int i; 223862306a36Sopenharmony_ci const char *mapped_names[6]; 223962306a36Sopenharmony_ci int valid_modes = 0; 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_ci /* HPI channel mode values can be from 1 to 6 224262306a36Sopenharmony_ci Some adapters only support a contiguous subset 224362306a36Sopenharmony_ci */ 224462306a36Sopenharmony_ci for (i = 0; i < HPI_CHANNEL_MODE_LAST; i++) 224562306a36Sopenharmony_ci if (!hpi_channel_mode_query_mode( 224662306a36Sopenharmony_ci h_control, i, &mode)) { 224762306a36Sopenharmony_ci mapped_names[valid_modes] = mode_names[mode]; 224862306a36Sopenharmony_ci valid_modes++; 224962306a36Sopenharmony_ci } 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci if (!valid_modes) 225262306a36Sopenharmony_ci return -EINVAL; 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci return snd_ctl_enum_info(uinfo, 1, valid_modes, mapped_names); 225562306a36Sopenharmony_ci} 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_cistatic int snd_asihpi_cmode_get(struct snd_kcontrol *kcontrol, 225862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 225962306a36Sopenharmony_ci{ 226062306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 226162306a36Sopenharmony_ci u16 mode; 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci if (hpi_channel_mode_get(h_control, &mode)) 226462306a36Sopenharmony_ci mode = 1; 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = mode - 1; 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_ci return 0; 226962306a36Sopenharmony_ci} 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_cistatic int snd_asihpi_cmode_put(struct snd_kcontrol *kcontrol, 227262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 227362306a36Sopenharmony_ci{ 227462306a36Sopenharmony_ci int change; 227562306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci change = 1; 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_ci hpi_handle_error(hpi_channel_mode_set(h_control, 228062306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] + 1)); 228162306a36Sopenharmony_ci return change; 228262306a36Sopenharmony_ci} 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_cistatic int snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi, 228662306a36Sopenharmony_ci struct hpi_control *hpi_ctl) 228762306a36Sopenharmony_ci{ 228862306a36Sopenharmony_ci struct snd_card *card = asihpi->card; 228962306a36Sopenharmony_ci struct snd_kcontrol_new snd_control; 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Mode"); 229262306a36Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; 229362306a36Sopenharmony_ci snd_control.info = snd_asihpi_cmode_info; 229462306a36Sopenharmony_ci snd_control.get = snd_asihpi_cmode_get; 229562306a36Sopenharmony_ci snd_control.put = snd_asihpi_cmode_put; 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 229862306a36Sopenharmony_ci} 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci/*------------------------------------------------------------ 230162306a36Sopenharmony_ci Sampleclock source controls 230262306a36Sopenharmony_ci ------------------------------------------------------------*/ 230362306a36Sopenharmony_cistatic const char * const sampleclock_sources[] = { 230462306a36Sopenharmony_ci "N/A", "Local PLL", "Digital Sync", "Word External", "Word Header", 230562306a36Sopenharmony_ci "SMPTE", "Digital1", "Auto", "Network", "Invalid", 230662306a36Sopenharmony_ci "Prev Module", "BLU-Link", 230762306a36Sopenharmony_ci "Digital2", "Digital3", "Digital4", "Digital5", 230862306a36Sopenharmony_ci "Digital6", "Digital7", "Digital8"}; 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci /* Number of strings must match expected enumerated values */ 231162306a36Sopenharmony_ci compile_time_assert( 231262306a36Sopenharmony_ci (ARRAY_SIZE(sampleclock_sources) == MAX_CLOCKSOURCES), 231362306a36Sopenharmony_ci assert_sampleclock_sources_size); 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_cistatic int snd_asihpi_clksrc_info(struct snd_kcontrol *kcontrol, 231662306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 231762306a36Sopenharmony_ci{ 231862306a36Sopenharmony_ci struct snd_card_asihpi *asihpi = 231962306a36Sopenharmony_ci (struct snd_card_asihpi *)(kcontrol->private_data); 232062306a36Sopenharmony_ci struct clk_cache *clkcache = &asihpi->cc; 232162306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 232262306a36Sopenharmony_ci uinfo->count = 1; 232362306a36Sopenharmony_ci uinfo->value.enumerated.items = clkcache->count; 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 232662306a36Sopenharmony_ci uinfo->value.enumerated.item = 232762306a36Sopenharmony_ci uinfo->value.enumerated.items - 1; 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci strcpy(uinfo->value.enumerated.name, 233062306a36Sopenharmony_ci clkcache->s[uinfo->value.enumerated.item].name); 233162306a36Sopenharmony_ci return 0; 233262306a36Sopenharmony_ci} 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_cistatic int snd_asihpi_clksrc_get(struct snd_kcontrol *kcontrol, 233562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 233662306a36Sopenharmony_ci{ 233762306a36Sopenharmony_ci struct snd_card_asihpi *asihpi = 233862306a36Sopenharmony_ci (struct snd_card_asihpi *)(kcontrol->private_data); 233962306a36Sopenharmony_ci struct clk_cache *clkcache = &asihpi->cc; 234062306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 234162306a36Sopenharmony_ci u16 source, srcindex = 0; 234262306a36Sopenharmony_ci int i; 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = 0; 234562306a36Sopenharmony_ci if (hpi_sample_clock_get_source(h_control, &source)) 234662306a36Sopenharmony_ci source = 0; 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT) 234962306a36Sopenharmony_ci if (hpi_sample_clock_get_source_index(h_control, &srcindex)) 235062306a36Sopenharmony_ci srcindex = 0; 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci for (i = 0; i < clkcache->count; i++) 235362306a36Sopenharmony_ci if ((clkcache->s[i].source == source) && 235462306a36Sopenharmony_ci (clkcache->s[i].index == srcindex)) 235562306a36Sopenharmony_ci break; 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci ucontrol->value.enumerated.item[0] = i; 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci return 0; 236062306a36Sopenharmony_ci} 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_cistatic int snd_asihpi_clksrc_put(struct snd_kcontrol *kcontrol, 236362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 236462306a36Sopenharmony_ci{ 236562306a36Sopenharmony_ci struct snd_card_asihpi *asihpi = 236662306a36Sopenharmony_ci (struct snd_card_asihpi *)(kcontrol->private_data); 236762306a36Sopenharmony_ci struct clk_cache *clkcache = &asihpi->cc; 236862306a36Sopenharmony_ci unsigned int item; 236962306a36Sopenharmony_ci int change; 237062306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci change = 1; 237362306a36Sopenharmony_ci item = ucontrol->value.enumerated.item[0]; 237462306a36Sopenharmony_ci if (item >= clkcache->count) 237562306a36Sopenharmony_ci item = clkcache->count-1; 237662306a36Sopenharmony_ci 237762306a36Sopenharmony_ci hpi_handle_error(hpi_sample_clock_set_source( 237862306a36Sopenharmony_ci h_control, clkcache->s[item].source)); 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci if (clkcache->s[item].source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT) 238162306a36Sopenharmony_ci hpi_handle_error(hpi_sample_clock_set_source_index( 238262306a36Sopenharmony_ci h_control, clkcache->s[item].index)); 238362306a36Sopenharmony_ci return change; 238462306a36Sopenharmony_ci} 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci/*------------------------------------------------------------ 238762306a36Sopenharmony_ci Clkrate controls 238862306a36Sopenharmony_ci ------------------------------------------------------------*/ 238962306a36Sopenharmony_ci/* Need to change this to enumerated control with list of rates */ 239062306a36Sopenharmony_cistatic int snd_asihpi_clklocal_info(struct snd_kcontrol *kcontrol, 239162306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 239262306a36Sopenharmony_ci{ 239362306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 239462306a36Sopenharmony_ci uinfo->count = 1; 239562306a36Sopenharmony_ci uinfo->value.integer.min = 8000; 239662306a36Sopenharmony_ci uinfo->value.integer.max = 192000; 239762306a36Sopenharmony_ci uinfo->value.integer.step = 100; 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_ci return 0; 240062306a36Sopenharmony_ci} 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_cistatic int snd_asihpi_clklocal_get(struct snd_kcontrol *kcontrol, 240362306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 240462306a36Sopenharmony_ci{ 240562306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 240662306a36Sopenharmony_ci u32 rate; 240762306a36Sopenharmony_ci u16 e; 240862306a36Sopenharmony_ci 240962306a36Sopenharmony_ci e = hpi_sample_clock_get_local_rate(h_control, &rate); 241062306a36Sopenharmony_ci if (!e) 241162306a36Sopenharmony_ci ucontrol->value.integer.value[0] = rate; 241262306a36Sopenharmony_ci else 241362306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 0; 241462306a36Sopenharmony_ci return 0; 241562306a36Sopenharmony_ci} 241662306a36Sopenharmony_ci 241762306a36Sopenharmony_cistatic int snd_asihpi_clklocal_put(struct snd_kcontrol *kcontrol, 241862306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 241962306a36Sopenharmony_ci{ 242062306a36Sopenharmony_ci int change; 242162306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci /* change = asihpi->mixer_clkrate[addr][0] != left || 242462306a36Sopenharmony_ci asihpi->mixer_clkrate[addr][1] != right; 242562306a36Sopenharmony_ci */ 242662306a36Sopenharmony_ci change = 1; 242762306a36Sopenharmony_ci hpi_handle_error(hpi_sample_clock_set_local_rate(h_control, 242862306a36Sopenharmony_ci ucontrol->value.integer.value[0])); 242962306a36Sopenharmony_ci return change; 243062306a36Sopenharmony_ci} 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_cistatic int snd_asihpi_clkrate_info(struct snd_kcontrol *kcontrol, 243362306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 243462306a36Sopenharmony_ci{ 243562306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 243662306a36Sopenharmony_ci uinfo->count = 1; 243762306a36Sopenharmony_ci uinfo->value.integer.min = 8000; 243862306a36Sopenharmony_ci uinfo->value.integer.max = 192000; 243962306a36Sopenharmony_ci uinfo->value.integer.step = 100; 244062306a36Sopenharmony_ci 244162306a36Sopenharmony_ci return 0; 244262306a36Sopenharmony_ci} 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_cistatic int snd_asihpi_clkrate_get(struct snd_kcontrol *kcontrol, 244562306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 244662306a36Sopenharmony_ci{ 244762306a36Sopenharmony_ci u32 h_control = kcontrol->private_value; 244862306a36Sopenharmony_ci u32 rate; 244962306a36Sopenharmony_ci u16 e; 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci e = hpi_sample_clock_get_sample_rate(h_control, &rate); 245262306a36Sopenharmony_ci if (!e) 245362306a36Sopenharmony_ci ucontrol->value.integer.value[0] = rate; 245462306a36Sopenharmony_ci else 245562306a36Sopenharmony_ci ucontrol->value.integer.value[0] = 0; 245662306a36Sopenharmony_ci return 0; 245762306a36Sopenharmony_ci} 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_cistatic int snd_asihpi_sampleclock_add(struct snd_card_asihpi *asihpi, 246062306a36Sopenharmony_ci struct hpi_control *hpi_ctl) 246162306a36Sopenharmony_ci{ 246262306a36Sopenharmony_ci struct snd_card *card; 246362306a36Sopenharmony_ci struct snd_kcontrol_new snd_control; 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_ci struct clk_cache *clkcache; 246662306a36Sopenharmony_ci u32 hSC = hpi_ctl->h_control; 246762306a36Sopenharmony_ci int has_aes_in = 0; 246862306a36Sopenharmony_ci int i, j; 246962306a36Sopenharmony_ci u16 source; 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_ci if (snd_BUG_ON(!asihpi)) 247262306a36Sopenharmony_ci return -EINVAL; 247362306a36Sopenharmony_ci card = asihpi->card; 247462306a36Sopenharmony_ci clkcache = &asihpi->cc; 247562306a36Sopenharmony_ci snd_control.private_value = hpi_ctl->h_control; 247662306a36Sopenharmony_ci 247762306a36Sopenharmony_ci clkcache->has_local = 0; 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci for (i = 0; i <= HPI_SAMPLECLOCK_SOURCE_LAST; i++) { 248062306a36Sopenharmony_ci if (hpi_sample_clock_query_source(hSC, 248162306a36Sopenharmony_ci i, &source)) 248262306a36Sopenharmony_ci break; 248362306a36Sopenharmony_ci clkcache->s[i].source = source; 248462306a36Sopenharmony_ci clkcache->s[i].index = 0; 248562306a36Sopenharmony_ci clkcache->s[i].name = sampleclock_sources[source]; 248662306a36Sopenharmony_ci if (source == HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT) 248762306a36Sopenharmony_ci has_aes_in = 1; 248862306a36Sopenharmony_ci if (source == HPI_SAMPLECLOCK_SOURCE_LOCAL) 248962306a36Sopenharmony_ci clkcache->has_local = 1; 249062306a36Sopenharmony_ci } 249162306a36Sopenharmony_ci if (has_aes_in) 249262306a36Sopenharmony_ci /* already will have picked up index 0 above */ 249362306a36Sopenharmony_ci for (j = 1; j < 8; j++) { 249462306a36Sopenharmony_ci if (hpi_sample_clock_query_source_index(hSC, 249562306a36Sopenharmony_ci j, HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT, 249662306a36Sopenharmony_ci &source)) 249762306a36Sopenharmony_ci break; 249862306a36Sopenharmony_ci clkcache->s[i].source = 249962306a36Sopenharmony_ci HPI_SAMPLECLOCK_SOURCE_AESEBU_INPUT; 250062306a36Sopenharmony_ci clkcache->s[i].index = j; 250162306a36Sopenharmony_ci clkcache->s[i].name = sampleclock_sources[ 250262306a36Sopenharmony_ci j+HPI_SAMPLECLOCK_SOURCE_LAST]; 250362306a36Sopenharmony_ci i++; 250462306a36Sopenharmony_ci } 250562306a36Sopenharmony_ci clkcache->count = i; 250662306a36Sopenharmony_ci 250762306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Source"); 250862306a36Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ; 250962306a36Sopenharmony_ci snd_control.info = snd_asihpi_clksrc_info; 251062306a36Sopenharmony_ci snd_control.get = snd_asihpi_clksrc_get; 251162306a36Sopenharmony_ci snd_control.put = snd_asihpi_clksrc_put; 251262306a36Sopenharmony_ci if (ctl_add(card, &snd_control, asihpi) < 0) 251362306a36Sopenharmony_ci return -EINVAL; 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci if (clkcache->has_local) { 251762306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Localrate"); 251862306a36Sopenharmony_ci snd_control.access = SNDRV_CTL_ELEM_ACCESS_READWRITE ; 251962306a36Sopenharmony_ci snd_control.info = snd_asihpi_clklocal_info; 252062306a36Sopenharmony_ci snd_control.get = snd_asihpi_clklocal_get; 252162306a36Sopenharmony_ci snd_control.put = snd_asihpi_clklocal_put; 252262306a36Sopenharmony_ci 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci if (ctl_add(card, &snd_control, asihpi) < 0) 252562306a36Sopenharmony_ci return -EINVAL; 252662306a36Sopenharmony_ci } 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci asihpi_ctl_init(&snd_control, hpi_ctl, "Rate"); 252962306a36Sopenharmony_ci snd_control.access = 253062306a36Sopenharmony_ci SNDRV_CTL_ELEM_ACCESS_VOLATILE | SNDRV_CTL_ELEM_ACCESS_READ; 253162306a36Sopenharmony_ci snd_control.info = snd_asihpi_clkrate_info; 253262306a36Sopenharmony_ci snd_control.get = snd_asihpi_clkrate_get; 253362306a36Sopenharmony_ci 253462306a36Sopenharmony_ci return ctl_add(card, &snd_control, asihpi); 253562306a36Sopenharmony_ci} 253662306a36Sopenharmony_ci/*------------------------------------------------------------ 253762306a36Sopenharmony_ci Mixer 253862306a36Sopenharmony_ci ------------------------------------------------------------*/ 253962306a36Sopenharmony_ci 254062306a36Sopenharmony_cistatic int snd_card_asihpi_mixer_new(struct snd_card_asihpi *asihpi) 254162306a36Sopenharmony_ci{ 254262306a36Sopenharmony_ci struct snd_card *card; 254362306a36Sopenharmony_ci unsigned int idx = 0; 254462306a36Sopenharmony_ci unsigned int subindex = 0; 254562306a36Sopenharmony_ci int err; 254662306a36Sopenharmony_ci struct hpi_control hpi_ctl, prev_ctl; 254762306a36Sopenharmony_ci 254862306a36Sopenharmony_ci if (snd_BUG_ON(!asihpi)) 254962306a36Sopenharmony_ci return -EINVAL; 255062306a36Sopenharmony_ci card = asihpi->card; 255162306a36Sopenharmony_ci strcpy(card->mixername, "Asihpi Mixer"); 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ci err = 255462306a36Sopenharmony_ci hpi_mixer_open(asihpi->hpi->adapter->index, 255562306a36Sopenharmony_ci &asihpi->h_mixer); 255662306a36Sopenharmony_ci hpi_handle_error(err); 255762306a36Sopenharmony_ci if (err) 255862306a36Sopenharmony_ci return -err; 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_ci memset(&prev_ctl, 0, sizeof(prev_ctl)); 256162306a36Sopenharmony_ci prev_ctl.control_type = -1; 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci for (idx = 0; idx < 2000; idx++) { 256462306a36Sopenharmony_ci err = hpi_mixer_get_control_by_index( 256562306a36Sopenharmony_ci asihpi->h_mixer, 256662306a36Sopenharmony_ci idx, 256762306a36Sopenharmony_ci &hpi_ctl.src_node_type, 256862306a36Sopenharmony_ci &hpi_ctl.src_node_index, 256962306a36Sopenharmony_ci &hpi_ctl.dst_node_type, 257062306a36Sopenharmony_ci &hpi_ctl.dst_node_index, 257162306a36Sopenharmony_ci &hpi_ctl.control_type, 257262306a36Sopenharmony_ci &hpi_ctl.h_control); 257362306a36Sopenharmony_ci if (err) { 257462306a36Sopenharmony_ci if (err == HPI_ERROR_CONTROL_DISABLED) { 257562306a36Sopenharmony_ci if (mixer_dump) 257662306a36Sopenharmony_ci dev_info(&asihpi->pci->dev, 257762306a36Sopenharmony_ci "Disabled HPI Control(%d)\n", 257862306a36Sopenharmony_ci idx); 257962306a36Sopenharmony_ci continue; 258062306a36Sopenharmony_ci } else 258162306a36Sopenharmony_ci break; 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci } 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_ci hpi_ctl.src_node_type -= HPI_SOURCENODE_NONE; 258662306a36Sopenharmony_ci hpi_ctl.dst_node_type -= HPI_DESTNODE_NONE; 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_ci /* ASI50xx in SSX mode has multiple meters on the same node. 258962306a36Sopenharmony_ci Use subindex to create distinct ALSA controls 259062306a36Sopenharmony_ci for any duplicated controls. 259162306a36Sopenharmony_ci */ 259262306a36Sopenharmony_ci if ((hpi_ctl.control_type == prev_ctl.control_type) && 259362306a36Sopenharmony_ci (hpi_ctl.src_node_type == prev_ctl.src_node_type) && 259462306a36Sopenharmony_ci (hpi_ctl.src_node_index == prev_ctl.src_node_index) && 259562306a36Sopenharmony_ci (hpi_ctl.dst_node_type == prev_ctl.dst_node_type) && 259662306a36Sopenharmony_ci (hpi_ctl.dst_node_index == prev_ctl.dst_node_index)) 259762306a36Sopenharmony_ci subindex++; 259862306a36Sopenharmony_ci else 259962306a36Sopenharmony_ci subindex = 0; 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci prev_ctl = hpi_ctl; 260262306a36Sopenharmony_ci 260362306a36Sopenharmony_ci switch (hpi_ctl.control_type) { 260462306a36Sopenharmony_ci case HPI_CONTROL_VOLUME: 260562306a36Sopenharmony_ci err = snd_asihpi_volume_add(asihpi, &hpi_ctl); 260662306a36Sopenharmony_ci break; 260762306a36Sopenharmony_ci case HPI_CONTROL_LEVEL: 260862306a36Sopenharmony_ci err = snd_asihpi_level_add(asihpi, &hpi_ctl); 260962306a36Sopenharmony_ci break; 261062306a36Sopenharmony_ci case HPI_CONTROL_MULTIPLEXER: 261162306a36Sopenharmony_ci err = snd_asihpi_mux_add(asihpi, &hpi_ctl); 261262306a36Sopenharmony_ci break; 261362306a36Sopenharmony_ci case HPI_CONTROL_CHANNEL_MODE: 261462306a36Sopenharmony_ci err = snd_asihpi_cmode_add(asihpi, &hpi_ctl); 261562306a36Sopenharmony_ci break; 261662306a36Sopenharmony_ci case HPI_CONTROL_METER: 261762306a36Sopenharmony_ci err = snd_asihpi_meter_add(asihpi, &hpi_ctl, subindex); 261862306a36Sopenharmony_ci break; 261962306a36Sopenharmony_ci case HPI_CONTROL_SAMPLECLOCK: 262062306a36Sopenharmony_ci err = snd_asihpi_sampleclock_add( 262162306a36Sopenharmony_ci asihpi, &hpi_ctl); 262262306a36Sopenharmony_ci break; 262362306a36Sopenharmony_ci case HPI_CONTROL_CONNECTION: /* ignore these */ 262462306a36Sopenharmony_ci continue; 262562306a36Sopenharmony_ci case HPI_CONTROL_TUNER: 262662306a36Sopenharmony_ci err = snd_asihpi_tuner_add(asihpi, &hpi_ctl); 262762306a36Sopenharmony_ci break; 262862306a36Sopenharmony_ci case HPI_CONTROL_AESEBU_TRANSMITTER: 262962306a36Sopenharmony_ci err = snd_asihpi_aesebu_tx_add(asihpi, &hpi_ctl); 263062306a36Sopenharmony_ci break; 263162306a36Sopenharmony_ci case HPI_CONTROL_AESEBU_RECEIVER: 263262306a36Sopenharmony_ci err = snd_asihpi_aesebu_rx_add(asihpi, &hpi_ctl); 263362306a36Sopenharmony_ci break; 263462306a36Sopenharmony_ci case HPI_CONTROL_VOX: 263562306a36Sopenharmony_ci case HPI_CONTROL_BITSTREAM: 263662306a36Sopenharmony_ci case HPI_CONTROL_MICROPHONE: 263762306a36Sopenharmony_ci case HPI_CONTROL_PARAMETRIC_EQ: 263862306a36Sopenharmony_ci case HPI_CONTROL_COMPANDER: 263962306a36Sopenharmony_ci default: 264062306a36Sopenharmony_ci if (mixer_dump) 264162306a36Sopenharmony_ci dev_info(&asihpi->pci->dev, 264262306a36Sopenharmony_ci "Untranslated HPI Control (%d) %d %d %d %d %d\n", 264362306a36Sopenharmony_ci idx, 264462306a36Sopenharmony_ci hpi_ctl.control_type, 264562306a36Sopenharmony_ci hpi_ctl.src_node_type, 264662306a36Sopenharmony_ci hpi_ctl.src_node_index, 264762306a36Sopenharmony_ci hpi_ctl.dst_node_type, 264862306a36Sopenharmony_ci hpi_ctl.dst_node_index); 264962306a36Sopenharmony_ci continue; 265062306a36Sopenharmony_ci } 265162306a36Sopenharmony_ci if (err < 0) 265262306a36Sopenharmony_ci return err; 265362306a36Sopenharmony_ci } 265462306a36Sopenharmony_ci if (HPI_ERROR_INVALID_OBJ_INDEX != err) 265562306a36Sopenharmony_ci hpi_handle_error(err); 265662306a36Sopenharmony_ci 265762306a36Sopenharmony_ci dev_info(&asihpi->pci->dev, "%d mixer controls found\n", idx); 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_ci return 0; 266062306a36Sopenharmony_ci} 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_ci/*------------------------------------------------------------ 266362306a36Sopenharmony_ci /proc interface 266462306a36Sopenharmony_ci ------------------------------------------------------------*/ 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_cistatic void 266762306a36Sopenharmony_cisnd_asihpi_proc_read(struct snd_info_entry *entry, 266862306a36Sopenharmony_ci struct snd_info_buffer *buffer) 266962306a36Sopenharmony_ci{ 267062306a36Sopenharmony_ci struct snd_card_asihpi *asihpi = entry->private_data; 267162306a36Sopenharmony_ci u32 h_control; 267262306a36Sopenharmony_ci u32 rate = 0; 267362306a36Sopenharmony_ci u16 source = 0; 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci u16 num_outstreams; 267662306a36Sopenharmony_ci u16 num_instreams; 267762306a36Sopenharmony_ci u16 version; 267862306a36Sopenharmony_ci u32 serial_number; 267962306a36Sopenharmony_ci u16 type; 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci int err; 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_ci snd_iprintf(buffer, "ASIHPI driver proc file\n"); 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ci hpi_handle_error(hpi_adapter_get_info(asihpi->hpi->adapter->index, 268662306a36Sopenharmony_ci &num_outstreams, &num_instreams, 268762306a36Sopenharmony_ci &version, &serial_number, &type)); 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_ci snd_iprintf(buffer, 269062306a36Sopenharmony_ci "Adapter type ASI%4X\nHardware Index %d\n" 269162306a36Sopenharmony_ci "%d outstreams\n%d instreams\n", 269262306a36Sopenharmony_ci type, asihpi->hpi->adapter->index, 269362306a36Sopenharmony_ci num_outstreams, num_instreams); 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci snd_iprintf(buffer, 269662306a36Sopenharmony_ci "Serial#%d\nHardware version %c%d\nDSP code version %03d\n", 269762306a36Sopenharmony_ci serial_number, ((version >> 3) & 0xf) + 'A', version & 0x7, 269862306a36Sopenharmony_ci ((version >> 13) * 100) + ((version >> 7) & 0x3f)); 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_ci err = hpi_mixer_get_control(asihpi->h_mixer, 270162306a36Sopenharmony_ci HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, 270262306a36Sopenharmony_ci HPI_CONTROL_SAMPLECLOCK, &h_control); 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_ci if (!err) { 270562306a36Sopenharmony_ci err = hpi_sample_clock_get_sample_rate(h_control, &rate); 270662306a36Sopenharmony_ci err += hpi_sample_clock_get_source(h_control, &source); 270762306a36Sopenharmony_ci 270862306a36Sopenharmony_ci if (!err) 270962306a36Sopenharmony_ci snd_iprintf(buffer, "Sample Clock %dHz, source %s\n", 271062306a36Sopenharmony_ci rate, sampleclock_sources[source]); 271162306a36Sopenharmony_ci } 271262306a36Sopenharmony_ci} 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_cistatic void snd_asihpi_proc_init(struct snd_card_asihpi *asihpi) 271562306a36Sopenharmony_ci{ 271662306a36Sopenharmony_ci snd_card_ro_proc_new(asihpi->card, "info", asihpi, 271762306a36Sopenharmony_ci snd_asihpi_proc_read); 271862306a36Sopenharmony_ci} 271962306a36Sopenharmony_ci 272062306a36Sopenharmony_ci/*------------------------------------------------------------ 272162306a36Sopenharmony_ci HWDEP 272262306a36Sopenharmony_ci ------------------------------------------------------------*/ 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_cistatic int snd_asihpi_hpi_open(struct snd_hwdep *hw, struct file *file) 272562306a36Sopenharmony_ci{ 272662306a36Sopenharmony_ci if (enable_hpi_hwdep) 272762306a36Sopenharmony_ci return 0; 272862306a36Sopenharmony_ci else 272962306a36Sopenharmony_ci return -ENODEV; 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_ci} 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_cistatic int snd_asihpi_hpi_release(struct snd_hwdep *hw, struct file *file) 273462306a36Sopenharmony_ci{ 273562306a36Sopenharmony_ci if (enable_hpi_hwdep) 273662306a36Sopenharmony_ci return asihpi_hpi_release(file); 273762306a36Sopenharmony_ci else 273862306a36Sopenharmony_ci return -ENODEV; 273962306a36Sopenharmony_ci} 274062306a36Sopenharmony_ci 274162306a36Sopenharmony_cistatic int snd_asihpi_hpi_ioctl(struct snd_hwdep *hw, struct file *file, 274262306a36Sopenharmony_ci unsigned int cmd, unsigned long arg) 274362306a36Sopenharmony_ci{ 274462306a36Sopenharmony_ci if (enable_hpi_hwdep) 274562306a36Sopenharmony_ci return asihpi_hpi_ioctl(file, cmd, arg); 274662306a36Sopenharmony_ci else 274762306a36Sopenharmony_ci return -ENODEV; 274862306a36Sopenharmony_ci} 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci/* results in /dev/snd/hwC#D0 file for each card with index # 275262306a36Sopenharmony_ci also /proc/asound/hwdep will contain '#-00: asihpi (HPI) for each card' 275362306a36Sopenharmony_ci*/ 275462306a36Sopenharmony_cistatic int snd_asihpi_hpi_new(struct snd_card_asihpi *asihpi, int device) 275562306a36Sopenharmony_ci{ 275662306a36Sopenharmony_ci struct snd_hwdep *hw; 275762306a36Sopenharmony_ci int err; 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci err = snd_hwdep_new(asihpi->card, "HPI", device, &hw); 276062306a36Sopenharmony_ci if (err < 0) 276162306a36Sopenharmony_ci return err; 276262306a36Sopenharmony_ci strcpy(hw->name, "asihpi (HPI)"); 276362306a36Sopenharmony_ci hw->iface = SNDRV_HWDEP_IFACE_LAST; 276462306a36Sopenharmony_ci hw->ops.open = snd_asihpi_hpi_open; 276562306a36Sopenharmony_ci hw->ops.ioctl = snd_asihpi_hpi_ioctl; 276662306a36Sopenharmony_ci hw->ops.release = snd_asihpi_hpi_release; 276762306a36Sopenharmony_ci hw->private_data = asihpi; 276862306a36Sopenharmony_ci return 0; 276962306a36Sopenharmony_ci} 277062306a36Sopenharmony_ci 277162306a36Sopenharmony_ci/*------------------------------------------------------------ 277262306a36Sopenharmony_ci CARD 277362306a36Sopenharmony_ci ------------------------------------------------------------*/ 277462306a36Sopenharmony_cistatic int snd_asihpi_probe(struct pci_dev *pci_dev, 277562306a36Sopenharmony_ci const struct pci_device_id *pci_id) 277662306a36Sopenharmony_ci{ 277762306a36Sopenharmony_ci int err; 277862306a36Sopenharmony_ci struct hpi_adapter *hpi; 277962306a36Sopenharmony_ci struct snd_card *card; 278062306a36Sopenharmony_ci struct snd_card_asihpi *asihpi; 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ci u32 h_control; 278362306a36Sopenharmony_ci u32 h_stream; 278462306a36Sopenharmony_ci u32 adapter_index; 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci static int dev; 278762306a36Sopenharmony_ci if (dev >= SNDRV_CARDS) 278862306a36Sopenharmony_ci return -ENODEV; 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci /* Should this be enable[hpi->index] ? */ 279162306a36Sopenharmony_ci if (!enable[dev]) { 279262306a36Sopenharmony_ci dev++; 279362306a36Sopenharmony_ci return -ENOENT; 279462306a36Sopenharmony_ci } 279562306a36Sopenharmony_ci 279662306a36Sopenharmony_ci /* Initialise low-level HPI driver */ 279762306a36Sopenharmony_ci err = asihpi_adapter_probe(pci_dev, pci_id); 279862306a36Sopenharmony_ci if (err < 0) 279962306a36Sopenharmony_ci return err; 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci hpi = pci_get_drvdata(pci_dev); 280262306a36Sopenharmony_ci adapter_index = hpi->adapter->index; 280362306a36Sopenharmony_ci /* first try to give the card the same index as its hardware index */ 280462306a36Sopenharmony_ci err = snd_card_new(&pci_dev->dev, adapter_index, id[adapter_index], 280562306a36Sopenharmony_ci THIS_MODULE, sizeof(struct snd_card_asihpi), &card); 280662306a36Sopenharmony_ci if (err < 0) { 280762306a36Sopenharmony_ci /* if that fails, try the default index==next available */ 280862306a36Sopenharmony_ci err = snd_card_new(&pci_dev->dev, index[dev], id[dev], 280962306a36Sopenharmony_ci THIS_MODULE, sizeof(struct snd_card_asihpi), 281062306a36Sopenharmony_ci &card); 281162306a36Sopenharmony_ci if (err < 0) 281262306a36Sopenharmony_ci return err; 281362306a36Sopenharmony_ci dev_warn(&pci_dev->dev, "Adapter index %d->ALSA index %d\n", 281462306a36Sopenharmony_ci adapter_index, card->number); 281562306a36Sopenharmony_ci } 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci asihpi = card->private_data; 281862306a36Sopenharmony_ci asihpi->card = card; 281962306a36Sopenharmony_ci asihpi->pci = pci_dev; 282062306a36Sopenharmony_ci asihpi->hpi = hpi; 282162306a36Sopenharmony_ci hpi->snd_card = card; 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_ci err = hpi_adapter_get_property(adapter_index, 282462306a36Sopenharmony_ci HPI_ADAPTER_PROPERTY_CAPS1, 282562306a36Sopenharmony_ci NULL, &asihpi->support_grouping); 282662306a36Sopenharmony_ci if (err) 282762306a36Sopenharmony_ci asihpi->support_grouping = 0; 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci err = hpi_adapter_get_property(adapter_index, 283062306a36Sopenharmony_ci HPI_ADAPTER_PROPERTY_CAPS2, 283162306a36Sopenharmony_ci &asihpi->support_mrx, NULL); 283262306a36Sopenharmony_ci if (err) 283362306a36Sopenharmony_ci asihpi->support_mrx = 0; 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_ci err = hpi_adapter_get_property(adapter_index, 283662306a36Sopenharmony_ci HPI_ADAPTER_PROPERTY_INTERVAL, 283762306a36Sopenharmony_ci NULL, &asihpi->update_interval_frames); 283862306a36Sopenharmony_ci if (err) 283962306a36Sopenharmony_ci asihpi->update_interval_frames = 512; 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci if (hpi->interrupt_mode) { 284262306a36Sopenharmony_ci asihpi->pcm_start = snd_card_asihpi_pcm_int_start; 284362306a36Sopenharmony_ci asihpi->pcm_stop = snd_card_asihpi_pcm_int_stop; 284462306a36Sopenharmony_ci hpi->interrupt_callback = snd_card_asihpi_isr; 284562306a36Sopenharmony_ci } else { 284662306a36Sopenharmony_ci asihpi->pcm_start = snd_card_asihpi_pcm_timer_start; 284762306a36Sopenharmony_ci asihpi->pcm_stop = snd_card_asihpi_pcm_timer_stop; 284862306a36Sopenharmony_ci } 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_ci hpi_handle_error(hpi_instream_open(adapter_index, 285162306a36Sopenharmony_ci 0, &h_stream)); 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_ci err = hpi_instream_host_buffer_free(h_stream); 285462306a36Sopenharmony_ci asihpi->can_dma = (!err); 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_ci hpi_handle_error(hpi_instream_close(h_stream)); 285762306a36Sopenharmony_ci 285862306a36Sopenharmony_ci if (!asihpi->can_dma) 285962306a36Sopenharmony_ci asihpi->update_interval_frames *= 2; 286062306a36Sopenharmony_ci 286162306a36Sopenharmony_ci err = hpi_adapter_get_property(adapter_index, 286262306a36Sopenharmony_ci HPI_ADAPTER_PROPERTY_CURCHANNELS, 286362306a36Sopenharmony_ci &asihpi->in_max_chans, &asihpi->out_max_chans); 286462306a36Sopenharmony_ci if (err) { 286562306a36Sopenharmony_ci asihpi->in_max_chans = 2; 286662306a36Sopenharmony_ci asihpi->out_max_chans = 2; 286762306a36Sopenharmony_ci } 286862306a36Sopenharmony_ci 286962306a36Sopenharmony_ci if (asihpi->out_max_chans > 2) { /* assume LL mode */ 287062306a36Sopenharmony_ci asihpi->out_min_chans = asihpi->out_max_chans; 287162306a36Sopenharmony_ci asihpi->in_min_chans = asihpi->in_max_chans; 287262306a36Sopenharmony_ci asihpi->support_grouping = 0; 287362306a36Sopenharmony_ci } else { 287462306a36Sopenharmony_ci asihpi->out_min_chans = 1; 287562306a36Sopenharmony_ci asihpi->in_min_chans = 1; 287662306a36Sopenharmony_ci } 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci dev_info(&pci_dev->dev, "Has dma:%d, grouping:%d, mrx:%d, uif:%d\n", 287962306a36Sopenharmony_ci asihpi->can_dma, 288062306a36Sopenharmony_ci asihpi->support_grouping, 288162306a36Sopenharmony_ci asihpi->support_mrx, 288262306a36Sopenharmony_ci asihpi->update_interval_frames 288362306a36Sopenharmony_ci ); 288462306a36Sopenharmony_ci 288562306a36Sopenharmony_ci err = snd_card_asihpi_pcm_new(asihpi, 0); 288662306a36Sopenharmony_ci if (err < 0) { 288762306a36Sopenharmony_ci dev_err(&pci_dev->dev, "pcm_new failed\n"); 288862306a36Sopenharmony_ci goto __nodev; 288962306a36Sopenharmony_ci } 289062306a36Sopenharmony_ci err = snd_card_asihpi_mixer_new(asihpi); 289162306a36Sopenharmony_ci if (err < 0) { 289262306a36Sopenharmony_ci dev_err(&pci_dev->dev, "mixer_new failed\n"); 289362306a36Sopenharmony_ci goto __nodev; 289462306a36Sopenharmony_ci } 289562306a36Sopenharmony_ci 289662306a36Sopenharmony_ci err = hpi_mixer_get_control(asihpi->h_mixer, 289762306a36Sopenharmony_ci HPI_SOURCENODE_CLOCK_SOURCE, 0, 0, 0, 289862306a36Sopenharmony_ci HPI_CONTROL_SAMPLECLOCK, &h_control); 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_ci if (!err) 290162306a36Sopenharmony_ci err = hpi_sample_clock_set_local_rate( 290262306a36Sopenharmony_ci h_control, adapter_fs); 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ci snd_asihpi_proc_init(asihpi); 290562306a36Sopenharmony_ci 290662306a36Sopenharmony_ci /* always create, can be enabled or disabled dynamically 290762306a36Sopenharmony_ci by enable_hwdep module param*/ 290862306a36Sopenharmony_ci snd_asihpi_hpi_new(asihpi, 0); 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_ci strcpy(card->driver, "ASIHPI"); 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci sprintf(card->shortname, "AudioScience ASI%4X", 291362306a36Sopenharmony_ci asihpi->hpi->adapter->type); 291462306a36Sopenharmony_ci sprintf(card->longname, "%s %i", 291562306a36Sopenharmony_ci card->shortname, adapter_index); 291662306a36Sopenharmony_ci err = snd_card_register(card); 291762306a36Sopenharmony_ci 291862306a36Sopenharmony_ci if (!err) { 291962306a36Sopenharmony_ci dev++; 292062306a36Sopenharmony_ci return 0; 292162306a36Sopenharmony_ci } 292262306a36Sopenharmony_ci__nodev: 292362306a36Sopenharmony_ci snd_card_free(card); 292462306a36Sopenharmony_ci dev_err(&pci_dev->dev, "snd_asihpi_probe error %d\n", err); 292562306a36Sopenharmony_ci return err; 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci} 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_cistatic void snd_asihpi_remove(struct pci_dev *pci_dev) 293062306a36Sopenharmony_ci{ 293162306a36Sopenharmony_ci struct hpi_adapter *hpi = pci_get_drvdata(pci_dev); 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_ci /* Stop interrupts */ 293462306a36Sopenharmony_ci if (hpi->interrupt_mode) { 293562306a36Sopenharmony_ci hpi->interrupt_callback = NULL; 293662306a36Sopenharmony_ci hpi_handle_error(hpi_adapter_set_property(hpi->adapter->index, 293762306a36Sopenharmony_ci HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0)); 293862306a36Sopenharmony_ci } 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci snd_card_free(hpi->snd_card); 294162306a36Sopenharmony_ci hpi->snd_card = NULL; 294262306a36Sopenharmony_ci asihpi_adapter_remove(pci_dev); 294362306a36Sopenharmony_ci} 294462306a36Sopenharmony_ci 294562306a36Sopenharmony_cistatic const struct pci_device_id asihpi_pci_tbl[] = { 294662306a36Sopenharmony_ci {HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_DSP6205, 294762306a36Sopenharmony_ci HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0, 294862306a36Sopenharmony_ci (kernel_ulong_t)HPI_6205}, 294962306a36Sopenharmony_ci {HPI_PCI_VENDOR_ID_TI, HPI_PCI_DEV_ID_PCI2040, 295062306a36Sopenharmony_ci HPI_PCI_VENDOR_ID_AUDIOSCIENCE, PCI_ANY_ID, 0, 0, 295162306a36Sopenharmony_ci (kernel_ulong_t)HPI_6000}, 295262306a36Sopenharmony_ci {0,} 295362306a36Sopenharmony_ci}; 295462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, asihpi_pci_tbl); 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_cistatic struct pci_driver driver = { 295762306a36Sopenharmony_ci .name = KBUILD_MODNAME, 295862306a36Sopenharmony_ci .id_table = asihpi_pci_tbl, 295962306a36Sopenharmony_ci .probe = snd_asihpi_probe, 296062306a36Sopenharmony_ci .remove = snd_asihpi_remove, 296162306a36Sopenharmony_ci}; 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_cistatic int __init snd_asihpi_init(void) 296462306a36Sopenharmony_ci{ 296562306a36Sopenharmony_ci asihpi_init(); 296662306a36Sopenharmony_ci return pci_register_driver(&driver); 296762306a36Sopenharmony_ci} 296862306a36Sopenharmony_ci 296962306a36Sopenharmony_cistatic void __exit snd_asihpi_exit(void) 297062306a36Sopenharmony_ci{ 297162306a36Sopenharmony_ci 297262306a36Sopenharmony_ci pci_unregister_driver(&driver); 297362306a36Sopenharmony_ci asihpi_exit(); 297462306a36Sopenharmony_ci} 297562306a36Sopenharmony_ci 297662306a36Sopenharmony_cimodule_init(snd_asihpi_init) 297762306a36Sopenharmony_cimodule_exit(snd_asihpi_exit) 297862306a36Sopenharmony_ci 2979