diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-23 12:49:28 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-23 12:49:28 -0700 |
commit | 1f3a8e093f470ef193b0ca6011d90180331c8b53 (patch) | |
tree | 66b7a58decabdc7f76ffb102899881c258c1df59 /drivers/staging/intel_sst | |
parent | c44dead70a841d90ddc01968012f323c33217c9e (diff) | |
parent | 1a4b6f66285785ddccef049e6b45be4e7c7a2189 (diff) |
Merge branch 'staging-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging-2.6
* 'staging-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging-2.6: (970 commits)
staging: usbip: replace usbip_u{dbg,err,info} and printk with dev_ and pr_
staging:iio: Trivial kconfig reorganization and uniformity improvements.
staging:iio:documenation partial update.
staging:iio: use pollfunc allocation helpers in remaining drivers.
staging:iio:max1363 misc cleanups and use of for_each_bit_set to simplify event code spitting out.
staging:iio: implement an iio_info structure to take some of the constant elements out of iio_dev.
staging:iio:meter:ade7758: Use private data space from iio_allocate_device
staging:iio:accel:lis3l02dq make write_reg_8 take value not a pointer to value.
staging:iio: ring core cleanups + check if read_last available in lis3l02dq
staging:iio:core cleanup: squash tiny wrappers and use dev_set_name to handle creation of event interface name.
staging:iio: poll func allocation clean up.
staging:iio:ad7780 trivial unused header cleanup.
staging:iio:adc: AD7780: Use private data space from iio_allocate_device + trivial fixes
staging:iio:adc:AD7780: Convert to new channel registration method
staging:iio:adc: AD7606: Drop dev_data in favour of iio_priv()
staging:iio:adc: AD7606: Consitently use indio_dev
staging:iio: Rip out helper for software rings.
staging:iio:adc:AD7298: Use private data space from iio_allocate_device
staging:iio: rationalization of different buffer implementation hooks.
staging:iio:imu:adis16400 avoid allocating rx, tx, and state separately from iio_dev.
...
Fix up trivial conflicts in
- drivers/staging/intel_sst/intelmid.c: patches applied in both branches
- drivers/staging/rt2860/common/cmm_data_{pci,usb}.c: removed vs spelling
- drivers/staging/usbip/vhci_sysfs.c: trivial header file inclusion
Diffstat (limited to 'drivers/staging/intel_sst')
23 files changed, 2131 insertions, 567 deletions
diff --git a/drivers/staging/intel_sst/intel_sst.c b/drivers/staging/intel_sst/intel_sst.c index 81c24d19eb9e..c0c144a2cda1 100644 --- a/drivers/staging/intel_sst/intel_sst.c +++ b/drivers/staging/intel_sst/intel_sst.c @@ -107,6 +107,9 @@ static irqreturn_t intel_sst_interrupt(int irq, void *context) unsigned int size = 0, str_id; struct stream_info *stream ; + /* Do not handle interrupt in suspended state */ + if (drv->sst_state == SST_SUSPENDED) + return IRQ_NONE; /* Interrupt arrived, check src */ isr.full = sst_shim_read(drv->shim, SST_ISRX); @@ -316,14 +319,30 @@ static int __devinit intel_sst_probe(struct pci_dev *pci, if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { ret = misc_register(&lpe_dev); if (ret) { - pr_err("couldn't register misc driver\n"); + pr_err("couldn't register LPE device\n"); goto do_free_misc; } + } else if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID) { + u32 csr; + + /*allocate mem for fw context save during suspend*/ + sst_drv_ctx->fw_cntx = kzalloc(FW_CONTEXT_MEM, GFP_KERNEL); + if (!sst_drv_ctx->fw_cntx) { + ret = -ENOMEM; + goto do_free_misc; + } + /*setting zero as that is valid mem to restore*/ + sst_drv_ctx->fw_cntx_size = 0; + + /*set lpe start clock and ram size*/ + csr = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr |= 0x30060; /*remove the clock ratio after fw fix*/ + sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr); } sst_drv_ctx->lpe_stalled = 0; - pm_runtime_set_active(&pci->dev); - pm_runtime_enable(&pci->dev); + pci_set_drvdata(pci, sst_drv_ctx); pm_runtime_allow(&pci->dev); + pm_runtime_put_noidle(&pci->dev); pr_debug("...successfully done!!!\n"); return ret; @@ -355,7 +374,8 @@ free_mad_wq: destroy_workqueue(sst_drv_ctx->mad_wq); do_free_drv_ctx: kfree(sst_drv_ctx); - pr_err("Probe failed with 0x%x\n", ret); + sst_drv_ctx = NULL; + pr_err("Probe failed with %d\n", ret); return ret; } @@ -369,35 +389,76 @@ do_free_drv_ctx: */ static void __devexit intel_sst_remove(struct pci_dev *pci) { + pm_runtime_get_noresume(&pci->dev); + pm_runtime_forbid(&pci->dev); pci_dev_put(sst_drv_ctx->pci); mutex_lock(&sst_drv_ctx->sst_lock); sst_drv_ctx->sst_state = SST_UN_INIT; mutex_unlock(&sst_drv_ctx->sst_lock); misc_deregister(&lpe_ctrl); - if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) - misc_deregister(&lpe_dev); free_irq(pci->irq, sst_drv_ctx); iounmap(sst_drv_ctx->dram); iounmap(sst_drv_ctx->iram); iounmap(sst_drv_ctx->mailbox); iounmap(sst_drv_ctx->shim); sst_drv_ctx->pmic_state = SND_MAD_UN_INIT; - if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) + if (sst_drv_ctx->pci_id == SST_MRST_PCI_ID) { + misc_deregister(&lpe_dev); kfree(sst_drv_ctx->mmap_mem); + } else + kfree(sst_drv_ctx->fw_cntx); flush_scheduled_work(); destroy_workqueue(sst_drv_ctx->process_reply_wq); destroy_workqueue(sst_drv_ctx->process_msg_wq); destroy_workqueue(sst_drv_ctx->post_msg_wq); destroy_workqueue(sst_drv_ctx->mad_wq); - kfree(sst_drv_ctx); - pci_release_region(pci, 1); - pci_release_region(pci, 2); - pci_release_region(pci, 3); - pci_release_region(pci, 4); - pci_release_region(pci, 5); + kfree(pci_get_drvdata(pci)); + sst_drv_ctx = NULL; + pci_release_regions(pci); + pci_disable_device(pci); pci_set_drvdata(pci, NULL); } +void sst_save_dsp_context(void) +{ + struct snd_sst_ctxt_params fw_context; + unsigned int pvt_id, i; + struct ipc_post *msg = NULL; + + /*check cpu type*/ + if (sst_drv_ctx->pci_id != SST_MFLD_PCI_ID) + return; + /*not supported for rest*/ + if (sst_drv_ctx->sst_state != SST_FW_RUNNING) { + pr_debug("fw not running no context save ...\n"); + return; + } + + /*send msg to fw*/ + if (sst_create_large_msg(&msg)) + return; + pvt_id = sst_assign_pvt_id(sst_drv_ctx); + i = sst_get_block_stream(sst_drv_ctx); + sst_drv_ctx->alloc_block[i].sst_id = pvt_id; + sst_fill_header(&msg->header, IPC_IA_GET_FW_CTXT, 1, pvt_id); + msg->header.part.data = sizeof(fw_context) + sizeof(u32); + fw_context.address = virt_to_phys((void *)sst_drv_ctx->fw_cntx); + fw_context.size = FW_CONTEXT_MEM; + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), + &fw_context, sizeof(fw_context)); + spin_lock(&sst_drv_ctx->list_spin_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + spin_unlock(&sst_drv_ctx->list_spin_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + /*wait for reply*/ + if (sst_wait_timeout(sst_drv_ctx, &sst_drv_ctx->alloc_block[i])) + pr_debug("err fw context save timeout ...\n"); + sst_drv_ctx->alloc_block[i].sst_id = BLOCK_UNINIT; + pr_debug("fw context saved ...\n"); + return; +} + /* Power Management */ /* * intel_sst_suspend - PCI suspend function @@ -417,6 +478,8 @@ int intel_sst_suspend(struct pci_dev *pci, pm_message_t state) pr_err("active streams,not able to suspend\n"); return -EBUSY; } + /*save fw context*/ + sst_save_dsp_context(); /*Assert RESET on LPE Processor*/ csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); csr.full = csr.full | 0x2; @@ -461,18 +524,45 @@ int intel_sst_resume(struct pci_dev *pci) return 0; } +/* The runtime_suspend/resume is pretty much similar to the legacy suspend/resume with the noted exception below: + * The PCI core takes care of taking the system through D3hot and restoring it back to D0 and so there is + * no need to duplicate that here. + */ static int intel_sst_runtime_suspend(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - pr_debug("runtime_suspend called\n"); - return intel_sst_suspend(pci_dev, PMSG_SUSPEND); + union config_status_reg csr; + + pr_debug("intel_sst_runtime_suspend called\n"); + if (sst_drv_ctx->stream_cnt) { + pr_err("active streams,not able to suspend\n"); + return -EBUSY; + } + /*save fw context*/ + sst_save_dsp_context(); + /*Assert RESET on LPE Processor*/ + csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr.full = csr.full | 0x2; + /* Move the SST state to Suspended */ + mutex_lock(&sst_drv_ctx->sst_lock); + sst_drv_ctx->sst_state = SST_SUSPENDED; + sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); + mutex_unlock(&sst_drv_ctx->sst_lock); + return 0; } static int intel_sst_runtime_resume(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); - pr_debug("runtime_resume called\n"); - return intel_sst_resume(pci_dev); + + pr_debug("intel_sst_runtime_resume called\n"); + if (sst_drv_ctx->sst_state != SST_SUSPENDED) { + pr_err("SST is not in suspended state\n"); + return 0; + } + + mutex_lock(&sst_drv_ctx->sst_lock); + sst_drv_ctx->sst_state = SST_UN_INIT; + mutex_unlock(&sst_drv_ctx->sst_lock); + return 0; } static int intel_sst_runtime_idle(struct device *dev) @@ -545,6 +635,7 @@ static void __exit intel_sst_exit(void) pci_unregister_driver(&driver); pr_debug("driver unloaded\n"); + sst_drv_ctx = NULL; return; } diff --git a/drivers/staging/intel_sst/intel_sst.h b/drivers/staging/intel_sst/intel_sst.h index cb03ff7d1a21..4ad2829105a7 100644 --- a/drivers/staging/intel_sst/intel_sst.h +++ b/drivers/staging/intel_sst/intel_sst.h @@ -30,9 +30,11 @@ * This file is shared between the SST and MAD drivers */ #include "intel_sst_ioctl.h" +#include <sound/jack.h> #define SST_CARD_NAMES "intel_mid_card" +#define MFLD_MAX_HW_CH 4 /* control list Pmic & Lpe */ /* Input controls */ enum port_status { @@ -82,12 +84,16 @@ struct snd_pmic_ops { int num_channel; int input_dev_id; int mute_status; - int pb_on; + struct mutex lock; + int pb_on, pbhs_on; int cap_on; int output_dev_id; + int lineout_dev_id, line_out_names_cnt; + int prev_lineout_dev_id; + bool jack_interrupt_status; int (*set_input_dev) (u8 value); int (*set_output_dev) (u8 value); - + int (*set_lineout_dev) (u8 value); int (*set_mute) (int dev_id, u8 value); int (*get_mute) (int dev_id, u8 *value); @@ -103,11 +109,30 @@ struct snd_pmic_ops { int (*power_up_pmic_pb) (unsigned int port); int (*power_up_pmic_cp) (unsigned int port); - int (*power_down_pmic_pb) (void); - int (*power_down_pmic_cp) (void); + int (*power_down_pmic_pb) (unsigned int device); + int (*power_down_pmic_cp) (unsigned int device); int (*power_down_pmic) (void); + void (*pmic_irq_cb) (void *cb_data, u8 value); + void (*pmic_irq_enable)(void *data); + int (*pmic_jack_enable) (void); + int (*pmic_get_mic_bias)(void *intelmaddata); + int (*pmic_set_headset_state)(int state); + + unsigned int hw_dmic_map[MFLD_MAX_HW_CH]; + unsigned int available_dmics; + int (*set_hw_dmic_route) (u8 index); + + int gpio_amp; }; +extern void sst_mad_send_jack_report(struct snd_jack *jack, + int buttonpressevent, + int status); + + +int intemad_set_headset_state(int state); +int intelmad_get_mic_bias(void); + struct intel_sst_pcm_control { int (*open) (struct snd_sst_params *str_param); int (*device_control) (int cmd, void *arg); diff --git a/drivers/staging/intel_sst/intel_sst_app_interface.c b/drivers/staging/intel_sst/intel_sst_app_interface.c index 1d0621260ea8..b8c7ddbd7cf3 100644 --- a/drivers/staging/intel_sst/intel_sst_app_interface.c +++ b/drivers/staging/intel_sst/intel_sst_app_interface.c @@ -418,10 +418,6 @@ static int snd_sst_fill_kernel_list(struct stream_info *stream, static int sent_offset; static unsigned long sent_index; - stream_bufs = kzalloc(sizeof(*stream_bufs), GFP_KERNEL); - if (!stream_bufs) - return -ENOMEM; - stream_bufs->addr = sst_drv_ctx->mmap_mem; #ifdef CONFIG_MRST_RAR_HANDLER if (stream->ops == STREAM_OPS_PLAYBACK_DRM) { for (index = stream->sg_index; index < nr_segs; index++) { @@ -448,6 +444,10 @@ static int snd_sst_fill_kernel_list(struct stream_info *stream, return retval; } #endif + stream_bufs = kzalloc(sizeof(*stream_bufs), GFP_KERNEL); + if (!stream_bufs) + return -ENOMEM; + stream_bufs->addr = sst_drv_ctx->mmap_mem; mmap_len = sst_drv_ctx->mmap_len; stream_bufs->addr = sst_drv_ctx->mmap_mem; bufp = stream->cur_ptr; @@ -961,6 +961,34 @@ free_mem: return retval; } + +int sst_ioctl_tuning_params(unsigned long arg) +{ + struct snd_sst_tuning_params params; + struct ipc_post *msg; + + if (copy_from_user(¶ms, (void __user *)arg, sizeof(params))) + return -EFAULT; + if (params.size > SST_MAILBOX_SIZE) + return -ENOMEM; + pr_debug("Parameter %d, Stream %d, Size %d\n", params.type, + params.str_id, params.size); + if (sst_create_large_msg(&msg)) + return -ENOMEM; + + sst_fill_header(&msg->header, IPC_IA_TUNING_PARAMS, 1, params.str_id); + msg->header.part.data = sizeof(u32) + sizeof(params) + params.size; + memcpy(msg->mailbox_data, &msg->header.full, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), ¶ms, sizeof(params)); + if (copy_from_user(msg->mailbox_data + sizeof(params), + (void __user *)(unsigned long)params.addr, + params.size)) { + kfree(msg->mailbox_data); + kfree(msg); + return -EFAULT; + } + return sst_send_algo_ipc(&msg); +} /** * intel_sst_ioctl - receives the device ioctl's * @file_ptr:pointer to file @@ -1412,6 +1440,15 @@ free_iobufs: } retval = intel_sst_ioctl_dsp(cmd, arg); break; + + case _IOC_NR(SNDRV_SST_TUNING_PARAMS): + if (minor != AM_MODULE) { + retval = -EBADRQC; + break; + } + retval = sst_ioctl_tuning_params(arg); + break; + default: retval = -EINVAL; } diff --git a/drivers/staging/intel_sst/intel_sst_common.h b/drivers/staging/intel_sst/intel_sst_common.h index 0a60e865b696..f8e9da6b3097 100644 --- a/drivers/staging/intel_sst/intel_sst_common.h +++ b/drivers/staging/intel_sst/intel_sst_common.h @@ -28,8 +28,8 @@ * Common private declarations for SST */ -#define SST_DRIVER_VERSION "1.2.09" -#define SST_VERSION_NUM 0x1209 +#define SST_DRIVER_VERSION "1.2.17" +#define SST_VERSION_NUM 0x1217 /* driver names */ #define SST_DRV_NAME "intel_sst_driver" @@ -37,6 +37,7 @@ #define SST_MFLD_PCI_ID 0x082F #define PCI_ID_LENGTH 4 #define SST_SUSPEND_DELAY 2000 +#define FW_CONTEXT_MEM (64*1024) enum sst_states { SST_FW_LOADED = 1, @@ -94,7 +95,7 @@ enum sst_ram_type { /* SST shim registers to structure mapping */ union config_status_reg { struct { - u32 rsvd0:1; + u32 mfld_strb:1; u32 sst_reset:1; u32 hw_rsvd:3; u32 sst_clk:2; @@ -417,6 +418,8 @@ struct intel_sst_drv { unsigned int audio_start; dev_t devt_d, devt_c; unsigned int max_streams; + unsigned int *fw_cntx; + unsigned int fw_cntx_size; }; extern struct intel_sst_drv *sst_drv_ctx; diff --git a/drivers/staging/intel_sst/intel_sst_drv_interface.c b/drivers/staging/intel_sst/intel_sst_drv_interface.c index 971588ce26d3..1021477f238d 100644 --- a/drivers/staging/intel_sst/intel_sst_drv_interface.c +++ b/drivers/staging/intel_sst/intel_sst_drv_interface.c @@ -105,21 +105,28 @@ void free_stream_context(unsigned int str_id) if (!sst_validate_strid(str_id)) { /* str_id is valid, so stream is alloacted */ stream = &sst_drv_ctx->streams[str_id]; + if (sst_free_stream(str_id)) + sst_clean_stream(&sst_drv_ctx->streams[str_id]); if (stream->ops == STREAM_OPS_PLAYBACK || stream->ops == STREAM_OPS_PLAYBACK_DRM) { sst_drv_ctx->pb_streams--; - if (sst_drv_ctx->pb_streams == 0) - sst_drv_ctx->scard_ops->power_down_pmic_pb(); + if (sst_drv_ctx->pci_id == SST_MFLD_PCI_ID) + sst_drv_ctx->scard_ops->power_down_pmic_pb( + stream->device); + else { + if (sst_drv_ctx->pb_streams == 0) + sst_drv_ctx->scard_ops-> + power_down_pmic_pb(stream->device); + } } else if (stream->ops == STREAM_OPS_CAPTURE) { sst_drv_ctx->cp_streams--; if (sst_drv_ctx->cp_streams == 0) - sst_drv_ctx->scard_ops->power_down_pmic_cp(); + sst_drv_ctx->scard_ops->power_down_pmic_cp( + stream->device); } if (sst_drv_ctx->pb_streams == 0 && sst_drv_ctx->cp_streams == 0) sst_drv_ctx->scard_ops->power_down_pmic(); - if (sst_free_stream(str_id)) - sst_clean_stream(&sst_drv_ctx->streams[str_id]); } } @@ -276,8 +283,8 @@ void sst_process_mad_ops(struct work_struct *work) retval = sst_resume_stream(mad_ops->stream_id); break; case SST_SND_DROP: -/* retval = sst_drop_stream(mad_ops->stream_id); -*/ break; + retval = sst_drop_stream(mad_ops->stream_id); + break; case SST_SND_START: pr_debug("SST Debug: start stream\n"); retval = sst_start_stream(mad_ops->stream_id); @@ -519,6 +526,9 @@ int register_sst_card(struct intel_sst_card_ops *card) pr_err("Repeat for registration..denied\n"); return -EBADRQC; } + /* The ASoC code doesn't set scard_ops */ + if (sst_drv_ctx->scard_ops) + sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT; return 0; } EXPORT_SYMBOL_GPL(register_sst_card); diff --git a/drivers/staging/intel_sst/intel_sst_dsp.c b/drivers/staging/intel_sst/intel_sst_dsp.c index bffe4c6e2928..a89e1ade8474 100644 --- a/drivers/staging/intel_sst/intel_sst_dsp.c +++ b/drivers/staging/intel_sst/intel_sst_dsp.c @@ -73,7 +73,8 @@ static int intel_sst_reset_dsp_medfield(void) union config_status_reg csr; pr_debug("Resetting the DSP in medfield\n"); - csr.full = 0x048303E2; + csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr.full |= 0x382; sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); return 0; @@ -109,11 +110,16 @@ static int sst_start_medfield(void) { union config_status_reg csr; - csr.full = 0x04830062; + csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr.part.bypass = 0; sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); - csr.full = 0x04830063; + csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr.part.mfld_strb = 1; sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); - csr.full = 0x04830061; + csr.full = sst_shim_read(sst_drv_ctx->shim, SST_CSR); + csr.part.run_stall = 0; + csr.part.sst_reset = 0; + pr_debug("Starting the DSP_medfld %x\n", csr.full); sst_shim_write(sst_drv_ctx->shim, SST_CSR, csr.full); pr_debug("Starting the DSP_medfld\n"); diff --git a/drivers/staging/intel_sst/intel_sst_fw_ipc.h b/drivers/staging/intel_sst/intel_sst_fw_ipc.h index 0f0c5bbc5f4b..5d0cc56aaef9 100644 --- a/drivers/staging/intel_sst/intel_sst_fw_ipc.h +++ b/drivers/staging/intel_sst/intel_sst_fw_ipc.h @@ -56,6 +56,8 @@ #define IPC_IA_GET_FW_VERSION 0x04 #define IPC_IA_GET_FW_BUILD_INF 0x05 #define IPC_IA_GET_FW_INFO 0x06 +#define IPC_IA_GET_FW_CTXT 0x07 +#define IPC_IA_SET_FW_CTXT 0x08 /* I2L Codec Config/control msgs */ #define IPC_IA_SET_CODEC_PARAMS 0x10 @@ -69,6 +71,7 @@ #define IPC_IA_DECODE_FRAMES 0x18 #define IPC_IA_ALG_PARAMS 0x1A +#define IPC_IA_TUNING_PARAMS 0x1B /* I2L Stream config/control msgs */ #define IPC_IA_ALLOC_STREAM 0x20 /* Allocate a stream ID */ @@ -406,4 +409,8 @@ struct ipc_post { char *mailbox_data; }; +struct snd_sst_ctxt_params { + u32 address; /* Physical Address in DDR where the context is stored */ + u32 size; /* size of the context */ +}; #endif /* __INTEL_SST_FW_IPC_H__ */ diff --git a/drivers/staging/intel_sst/intel_sst_ioctl.h b/drivers/staging/intel_sst/intel_sst_ioctl.h index bebc395a3c1f..5da5ee092c69 100644 --- a/drivers/staging/intel_sst/intel_sst_ioctl.h +++ b/drivers/staging/intel_sst/intel_sst_ioctl.h @@ -400,6 +400,13 @@ struct snd_sst_dbufs { struct snd_sst_buffs *obufs; }; +struct snd_sst_tuning_params { + __u8 type; + __u8 str_id; + __u8 size; + __u8 rsvd; + __aligned_u64 addr; +} __attribute__ ((packed)); /*IOCTL defined here */ /*SST MMF IOCTLS only */ #define SNDRV_SST_STREAM_SET_PARAMS _IOR('L', 0x00, \ @@ -428,5 +435,6 @@ struct snd_sst_dbufs { /*DSP Ioctls on /dev/intel_sst_ctrl only*/ #define SNDRV_SST_SET_ALGO _IOW('L', 0x30, struct snd_ppp_params *) #define SNDRV_SST_GET_ALGO _IOWR('L', 0x31, struct snd_ppp_params *) +#define SNDRV_SST_TUNING_PARAMS _IOW('L', 0x32, struct snd_sst_tuning_params *) #endif /* __INTEL_SST_IOCTL_H__ */ diff --git a/drivers/staging/intel_sst/intel_sst_ipc.c b/drivers/staging/intel_sst/intel_sst_ipc.c index 0742dde2685d..5c3444f6ab41 100644 --- a/drivers/staging/intel_sst/intel_sst_ipc.c +++ b/drivers/staging/intel_sst/intel_sst_ipc.c @@ -154,6 +154,37 @@ void sst_clear_interrupt(void) sst_shim_write(sst_drv_ctx->shim, SST_IMRX, imr.full); } +void sst_restore_fw_context(void) +{ + struct snd_sst_ctxt_params fw_context; + struct ipc_post *msg = NULL; + + pr_debug("restore_fw_context\n"); + /*check cpu type*/ + if (sst_drv_ctx->pci_id != SST_MFLD_PCI_ID) + return; + /*not supported for rest*/ + if (!sst_drv_ctx->fw_cntx_size) + return; + /*nothing to restore*/ + pr_debug("restoring context......\n"); + /*send msg to fw*/ + if (sst_create_large_msg(&msg)) + return; + + sst_fill_header(&msg->header, IPC_IA_SET_FW_CTXT, 1, 0); + msg->header.part.data = sizeof(fw_context) + sizeof(u32); + fw_context.address = virt_to_phys((void *)sst_drv_ctx->fw_cntx); + fw_context.size = sst_drv_ctx->fw_cntx_size; + memcpy(msg->mailbox_data, &msg->header, sizeof(u32)); + memcpy(msg->mailbox_data + sizeof(u32), + &fw_context, sizeof(fw_context)); + spin_lock(&sst_drv_ctx->list_spin_lock); + list_add_tail(&msg->node, &sst_drv_ctx->ipc_dispatch_list); + spin_unlock(&sst_drv_ctx->list_spin_lock); + sst_post_message(&sst_drv_ctx->ipc_post_msg_wq); + return; +} /* * process_fw_init - process the FW init msg * @@ -184,13 +215,13 @@ int process_fw_init(struct sst_ipc_msg_wq *msg) sst_drv_ctx->sst_state = SST_FW_RUNNING; sst_drv_ctx->lpe_stalled = 0; mutex_unlock(&sst_drv_ctx->sst_lock); - pr_debug("FW Version %x.%x\n", - init->fw_version.major, init->fw_version.minor); - pr_debug("Build No %x Type %x\n", - init->fw_version.build, init->fw_version.type); + pr_debug("FW Version %02x.%02x.%02x\n", init->fw_version.major, + init->fw_version.minor, init->fw_version.build); + pr_debug("Build Type %x\n", init->fw_version.type); pr_debug(" Build date %s Time %s\n", init->build_info.date, init->build_info.time); sst_wake_up_alloc_block(sst_drv_ctx, FW_DWNL_ID, retval, NULL); + sst_restore_fw_context(); return retval; } /** @@ -385,6 +416,24 @@ void sst_process_reply(struct work_struct *work) } break; } + + case IPC_IA_TUNING_PARAMS: { + pr_debug("sst:IPC_TUNING_PARAMS resp: %x\n", msg->header.full); + pr_debug("data value %x\n", msg->header.part.data); + if (msg->header.part.large) { + pr_debug("alg set failed\n"); + sst_drv_ctx->ppp_params_blk.ret_code = + -msg->header.part.data; + } else { + pr_debug("alg set success\n"); + sst_drv_ctx->ppp_params_blk.ret_code = 0; + } + if (sst_drv_ctx->ppp_params_blk.on == true) { + sst_drv_ctx->ppp_params_blk.condition = true; + wake_up(&sst_drv_ctx->wait_queue); + } + } + case IPC_IA_GET_FW_INFO: { struct snd_sst_fw_info *fw_info = (struct snd_sst_fw_info *)msg->mailbox; @@ -615,12 +664,18 @@ void sst_process_reply(struct work_struct *work) break; case IPC_IA_FREE_STREAM: + str_info = &sst_drv_ctx->streams[str_id]; if (!msg->header.part.data) { pr_debug("Stream %d freed\n", str_id); } else { pr_err("Free for %d ret error %x\n", str_id, msg->header.part.data); } + if (str_info->ctrl_blk.on == true) { + str_info->ctrl_blk.on = false; + str_info->ctrl_blk.condition = true; + wake_up(&sst_drv_ctx->wait_queue); + } break; case IPC_IA_ALLOC_STREAM: { /* map to stream, call play */ @@ -699,6 +754,17 @@ void sst_process_reply(struct work_struct *work) case IPC_IA_START_STREAM: pr_debug("reply for START STREAM %x\n", msg->header.full); break; + + case IPC_IA_GET_FW_CTXT: + pr_debug("reply for get fw ctxt %x\n", msg->header.full); + if (msg->header.part.data) + sst_drv_ctx->fw_cntx_size = 0; + else + sst_drv_ctx->fw_cntx_size = *sst_drv_ctx->fw_cntx; + pr_debug("fw copied data %x\n", sst_drv_ctx->fw_cntx_size); + sst_wake_up_alloc_block( + sst_drv_ctx, str_id, msg->header.part.data, NULL); + break; default: /* Illegal case */ pr_err("process reply:default = %x\n", msg->header.full); diff --git a/drivers/staging/intel_sst/intel_sst_pvt.c b/drivers/staging/intel_sst/intel_sst_pvt.c index 01f8c3b1cf74..e034bea56f14 100644 --- a/drivers/staging/intel_sst/intel_sst_pvt.c +++ b/drivers/staging/intel_sst/intel_sst_pvt.c @@ -203,7 +203,7 @@ int sst_create_large_msg(struct ipc_post **arg) kfree(msg); pr_err("kzalloc mailbox_data failed"); return -ENOMEM; - }; + } *arg = msg; return 0; } diff --git a/drivers/staging/intel_sst/intel_sst_stream.c b/drivers/staging/intel_sst/intel_sst_stream.c index dd58be5b1975..be4565e74f8c 100644 --- a/drivers/staging/intel_sst/intel_sst_stream.c +++ b/drivers/staging/intel_sst/intel_sst_stream.c @@ -31,6 +31,7 @@ #include <linux/pci.h> #include <linux/firmware.h> #include <linux/sched.h> +#include <linux/delay.h> #include "intel_sst_ioctl.h" #include "intel_sst.h" #include "intel_sst_fw_ipc.h" @@ -47,7 +48,7 @@ */ int sst_check_device_type(u32 device, u32 num_chan, u32 *pcm_slot) { - if (device > MAX_NUM_STREAMS_MFLD) { + if (device >= MAX_NUM_STREAMS_MFLD) { pr_debug("device type invalid %d\n", device); return -EINVAL; } @@ -72,6 +73,8 @@ int sst_check_device_type(u32 device, u32 num_chan, u32 *pcm_slot) *pcm_slot = 0x07; else if (device == SND_SST_DEVICE_CAPTURE && num_chan == 4) *pcm_slot = 0x0F; + else if (device == SND_SST_DEVICE_CAPTURE && num_chan > 4) + *pcm_slot = 0x1F; else { pr_debug("No condition satisfied.. ret err\n"); return -EINVAL; @@ -519,10 +522,6 @@ int sst_drain_stream(int str_id) str_info->data_blk.on = true; retval = sst_wait_interruptible(sst_drv_ctx, &str_info->data_blk); str_info->need_draining = false; - if (retval == -SST_ERR_INVALID_STREAM_ID) { - retval = -EINVAL; - sst_clean_stream(str_info); - } return retval; } @@ -563,6 +562,12 @@ int sst_free_stream(int str_id) str_info->data_blk.ret_code = 0; wake_up(&sst_drv_ctx->wait_queue); } + str_info->data_blk.on = true; + str_info->data_blk.condition = false; + retval = sst_wait_interruptible_timeout(sst_drv_ctx, + &str_info->ctrl_blk, SST_BLOCK_TIMEOUT); + pr_debug("wait for free returned %d\n", retval); + msleep(100); mutex_lock(&sst_drv_ctx->stream_lock); sst_clean_stream(str_info); mutex_unlock(&sst_drv_ctx->stream_lock); diff --git a/drivers/staging/intel_sst/intel_sst_stream_encoded.c b/drivers/staging/intel_sst/intel_sst_stream_encoded.c index d5f07b878828..2be58c5cba02 100644 --- a/drivers/staging/intel_sst/intel_sst_stream_encoded.c +++ b/drivers/staging/intel_sst/intel_sst_stream_encoded.c @@ -363,7 +363,6 @@ int sst_parse_target(struct snd_sst_slot_info *slot) pr_err("SST_Activate_target_fail\n"); else pr_err("SST_Activate_target_pass\n"); - return retval; } else if (slot->action == SND_SST_PORT_PREPARE && slot->device_type == SND_SST_DEVICE_PCM) { retval = sst_prepare_target(slot); @@ -371,12 +370,11 @@ int sst_parse_target(struct snd_sst_slot_info *slot) pr_err("SST_prepare_target_fail\n"); else pr_err("SST_prepare_target_pass\n"); - return retval; } else { pr_err("slot_action : %d, device_type: %d\n", slot->action, slot->device_type); - return retval; } + return retval; } int sst_send_target(struct snd_sst_target_device *target) @@ -886,8 +884,7 @@ static int sst_prepare_input_buffers_rar(struct stream_info *str_info, int *input_index, int *in_copied, int *input_index_valid_size, int *new_entry_flag) { - int retval = 0; - int i; + int retval = 0, i; if (str_info->ops == STREAM_OPS_PLAYBACK_DRM) { struct RAR_buffer rar_buffers; @@ -924,7 +921,6 @@ static int sst_prepare_input_buffers_rar(struct stream_info *str_info, return retval; } #endif - /*This function is used to prepare the kernel input buffers with contents before sending for decode*/ static int sst_prepare_input_buffers(struct stream_info *str_info, diff --git a/drivers/staging/intel_sst/intelmid.c b/drivers/staging/intel_sst/intelmid.c index ebb6d03552c4..25656ad2802e 100644 --- a/drivers/staging/intel_sst/intelmid.c +++ b/drivers/staging/intel_sst/intelmid.c @@ -33,17 +33,20 @@ #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/firmware.h> +#include <linux/input.h> #include <sound/control.h> #include <asm/mrst.h> #include <sound/pcm.h> -#include "jack.h" +#include <sound/jack.h> #include <sound/pcm_params.h> #include <sound/initval.h> +#include <linux/gpio.h> #include "intel_sst.h" #include "intel_sst_ioctl.h" #include "intel_sst_fw_ipc.h" #include "intel_sst_common.h" #include "intelmid_snd_control.h" +#include "intelmid_adc_control.h" #include "intelmid.h" MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); @@ -65,7 +68,14 @@ MODULE_PARM_DESC(card_id, "ID string for INTELMAD soundcard."); int sst_card_vendor_id; int intelmid_audio_interrupt_enable;/*checkpatch fix*/ - +struct snd_intelmad *intelmad_drv; + +#define INFO(_cpu_id, _irq_cache, _size) \ + ((kernel_ulong_t)&(struct snd_intelmad_probe_info) { \ + .cpu_id = (_cpu_id), \ + .irq_cache = (_irq_cache), \ + .size = (_size), \ + }) /* Data path functionalities */ static struct snd_pcm_hardware snd_intelmad_stream = { .info = (SNDRV_PCM_INFO_INTERLEAVED | @@ -187,7 +197,7 @@ static int snd_intelmad_pcm_prepare(struct snd_pcm_substream *substream) return ret_val; } - ret_val = snd_intelmad_alloc_stream(substream); + ret_val = snd_intelmad_alloc_stream(substream); if (ret_val < 0) return ret_val; stream->dbg_cum_bytes = 0; @@ -326,6 +336,16 @@ static int snd_intelmad_open(struct snd_pcm_substream *substream, runtime = substream->runtime; /* set the runtime hw parameter with local snd_pcm_hardware struct */ runtime->hw = snd_intelmad_stream; + if (intelmaddata->cpu_id == CPU_CHIP_LINCROFT) { + /* + * MRST firmware currently denies stereo recording requests. + */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + runtime->hw.formats = (SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_U16); + runtime->hw.channels_max = 1; + } + } if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) { runtime->hw = snd_intelmad_stream; runtime->hw.rates = SNDRV_PCM_RATE_48000; @@ -426,7 +446,55 @@ static struct snd_pcm_ops snd_intelmad_capture_ops = { .pointer = snd_intelmad_pcm_pointer, }; +int intelmad_get_mic_bias(void) +{ + struct snd_pmic_ops *pmic_ops; + + if (!intelmad_drv || !intelmad_drv->sstdrv_ops) + return -ENODEV; + pmic_ops = intelmad_drv->sstdrv_ops->scard_ops; + if (pmic_ops && pmic_ops->pmic_get_mic_bias) + return pmic_ops->pmic_get_mic_bias(intelmad_drv); + else + return -ENODEV; +} +EXPORT_SYMBOL_GPL(intelmad_get_mic_bias); + +int intelmad_set_headset_state(int state) +{ + struct snd_pmic_ops *pmic_ops; + + if (!intelmad_drv || !intelmad_drv->sstdrv_ops) + return -ENODEV; + pmic_ops = intelmad_drv->sstdrv_ops->scard_ops; + if (pmic_ops && pmic_ops->pmic_set_headset_state) + return pmic_ops->pmic_set_headset_state(state); + else + return -ENODEV; +} +EXPORT_SYMBOL_GPL(intelmad_set_headset_state); + +void sst_process_mad_jack_detection(struct work_struct *work) +{ + u8 interrupt_status; + struct mad_jack_msg_wq *mad_jack_detect = + container_of(work, struct mad_jack_msg_wq, wq); + + struct snd_intelmad *intelmaddata = + mad_jack_detect->intelmaddata; + if (!intelmaddata) + return; + + interrupt_status = mad_jack_detect->intsts; + if (intelmaddata->sstdrv_ops && intelmaddata->sstdrv_ops->scard_ops + && intelmaddata->sstdrv_ops->scard_ops->pmic_irq_cb) { + intelmaddata->sstdrv_ops->scard_ops->pmic_irq_cb( + (void *)intelmaddata, interrupt_status); + intelmaddata->sstdrv_ops->scard_ops->pmic_jack_enable(); + } + kfree(mad_jack_detect); +} /** * snd_intelmad_intr_handler- interrupt handler * @@ -439,15 +507,17 @@ static irqreturn_t snd_intelmad_intr_handler(int irq, void *dev) { struct snd_intelmad *intelmaddata = (struct snd_intelmad *)dev; - u8 intsts; - - memcpy_fromio(&intsts, + u8 interrupt_status; + struct mad_jack_msg_wq *mad_jack_msg; + memcpy_fromio(&interrupt_status, ((void *)(intelmaddata->int_base)), sizeof(u8)); - intelmaddata->mad_jack_msg.intsts = intsts; - intelmaddata->mad_jack_msg.intelmaddata = intelmaddata; - queue_work(intelmaddata->mad_jack_wq, &intelmaddata->mad_jack_msg.wq); + mad_jack_msg = kzalloc(sizeof(*mad_jack_msg), GFP_ATOMIC); + mad_jack_msg->intsts = interrupt_status; + mad_jack_msg->intelmaddata = intelmaddata; + INIT_WORK(&mad_jack_msg->wq, sst_process_mad_jack_detection); + queue_work(intelmaddata->mad_jack_wq, &mad_jack_msg->wq); return IRQ_HANDLED; } @@ -460,286 +530,22 @@ void sst_mad_send_jack_report(struct snd_jack *jack, pr_debug("MAD error jack empty\n"); } else { - pr_debug("MAD send jack report for = %d!!!\n", status); - pr_debug("MAD send jack report %d\n", jack->type); snd_jack_report(jack, status); - - /*button pressed and released */ + /* button pressed and released */ if (buttonpressevent) snd_jack_report(jack, 0); pr_debug("MAD sending jack report Done !!!\n"); } - - - -} - -void sst_mad_jackdetection_fs(u8 intsts , struct snd_intelmad *intelmaddata) -{ - struct snd_jack *jack = NULL; - unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; - struct sc_reg_access sc_access[] = { - {0x187, 0x00, MASK7}, - {0x188, 0x10, MASK4}, - {0x18b, 0x10, MASK4}, - }; - - struct sc_reg_access sc_access_write[] = { - {0x198, 0x00, 0x0}, - }; - - if (intsts & 0x4) { - - if (!(intelmid_audio_interrupt_enable)) { - pr_debug("Audio interrupt enable\n"); - sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3); - - sst_sc_reg_access(sc_access_write, PMIC_WRITE, 1); - intelmid_audio_interrupt_enable = 1; - intelmaddata->jack[0].jack_status = 0; - intelmaddata->jack[1].jack_status = 0; - - } - /* send headphone detect */ - pr_debug("MAD headphone %d\n", intsts & 0x4); - jack = &intelmaddata->jack[0].jack; - present = !(intelmaddata->jack[0].jack_status); - intelmaddata->jack[0].jack_status = present; - jack_event_flag = 1; - - } - - if (intsts & 0x2) { - /* send short push */ - pr_debug("MAD short push %d\n", intsts & 0x2); - jack = &intelmaddata->jack[2].jack; - present = 1; - jack_event_flag = 1; - buttonpressflag = 1; - } - if (intsts & 0x1) { - /* send long push */ - pr_debug("MAD long push %d\n", intsts & 0x1); - jack = &intelmaddata->jack[3].jack; - present = 1; - jack_event_flag = 1; - buttonpressflag = 1; - } - if (intsts & 0x8) { - if (!(intelmid_audio_interrupt_enable)) { - pr_debug("Audio interrupt enable\n"); - sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3); - - sst_sc_reg_access(sc_access_write, PMIC_WRITE, 1); - intelmid_audio_interrupt_enable = 1; - intelmaddata->jack[0].jack_status = 0; - intelmaddata->jack[1].jack_status = 0; - } - /* send headset detect */ - pr_debug("MAD headset = %d\n", intsts & 0x8); - jack = &intelmaddata->jack[1].jack; - present = !(intelmaddata->jack[1].jack_status); - intelmaddata->jack[1].jack_status = present; - jack_event_flag = 1; - } - - if (jack_event_flag) - sst_mad_send_jack_report(jack, buttonpressflag, present); -} - - -void sst_mad_jackdetection_mx(u8 intsts, struct snd_intelmad *intelmaddata) -{ - u8 value = 0, jack_prev_state = 0; - struct snd_jack *jack = NULL; - unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; - time_t timediff; - struct sc_reg_access sc_access_read = {0,}; - struct snd_pmic_ops *scard_ops; - - scard_ops = intelmaddata->sstdrv_ops->scard_ops; - - pr_debug("previous value: %x\n", intelmaddata->jack_prev_state); - - if (!(intelmid_audio_interrupt_enable)) { - pr_debug("Audio interrupt enable\n"); - intelmaddata->jack_prev_state = 0xC0; - intelmid_audio_interrupt_enable = 1; - } - - if (intsts & 0x2) { - jack_prev_state = intelmaddata->jack_prev_state; - if (intelmaddata->pmic_status == PMIC_INIT) { - sc_access_read.reg_addr = 0x201; - sst_sc_reg_access(&sc_access_read, PMIC_READ, 1); - value = (sc_access_read.value); - pr_debug("value returned = 0x%x\n", value); - } - - if (jack_prev_state == 0xc0 && value == 0x40) { - /*headset detected. */ - pr_debug("MAD headset inserted\n"); - jack = &intelmaddata->jack[1].jack; - present = 1; - jack_event_flag = 1; - intelmaddata->jack[1].jack_status = 1; - - } - - if (jack_prev_state == 0xc0 && value == 0x00) { - /* headphone detected. */ - pr_debug("MAD headphone inserted\n"); - jack = &intelmaddata->jack[0].jack; - present = 1; - jack_event_flag = 1; - - } - - if (jack_prev_state == 0x40 && value == 0xc0) { - /*headset removed*/ - pr_debug("Jack headset status %d\n", - intelmaddata->jack[1].jack_status); - pr_debug("MAD headset removed\n"); - jack = &intelmaddata->jack[1].jack; - present = 0; - jack_event_flag = 1; - intelmaddata->jack[1].jack_status = 0; - } - - if (jack_prev_state == 0x00 && value == 0xc0) { - /* headphone detected. */ - pr_debug("Jack headphone status %d\n", - intelmaddata->jack[0].jack_status); - pr_debug("headphone removed\n"); - jack = &intelmaddata->jack[0].jack; - present = 0; - jack_event_flag = 1; - } - - if (jack_prev_state == 0x40 && value == 0x00) { - /*button pressed*/ - do_gettimeofday(&intelmaddata->jack[1].buttonpressed); - pr_debug("MAD button press detected\n"); - } - - - if (jack_prev_state == 0x00 && value == 0x40) { - if (intelmaddata->jack[1].jack_status) { - /*button pressed*/ - do_gettimeofday( - &intelmaddata->jack[1].buttonreleased); - /*button pressed */ - pr_debug("Button Released detected\n"); - timediff = intelmaddata->jack[1]. - buttonreleased.tv_sec - intelmaddata-> - jack[1].buttonpressed.tv_sec; - buttonpressflag = 1; - if (timediff > 1) { - pr_debug("long press detected\n"); - /* send headphone detect/undetect */ - jack = &intelmaddata->jack[3].jack; - present = 1; - jack_event_flag = 1; - } else { - pr_debug("short press detected\n"); - /* send headphone detect/undetect */ - jack = &intelmaddata->jack[2].jack; - present = 1; - jack_event_flag = 1; - } - } - - } - intelmaddata->jack_prev_state = value; - } - if (jack_event_flag) - sst_mad_send_jack_report(jack, buttonpressflag, present); -} - - -void sst_mad_jackdetection_nec(u8 intsts, struct snd_intelmad *intelmaddata) -{ - u8 value = 0; - struct snd_jack *jack = NULL; - unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; - struct sc_reg_access sc_access_read = {0,}; - - if (intelmaddata->pmic_status == PMIC_INIT) { - sc_access_read.reg_addr = 0x132; - sst_sc_reg_access(&sc_access_read, PMIC_READ, 1); - value = (sc_access_read.value); - pr_debug("value returned = 0x%x\n", value); - } - if (intsts & 0x1) { - pr_debug("headset detected\n"); - /* send headset detect/undetect */ - jack = &intelmaddata->jack[1].jack; - present = (value == 0x1) ? 1 : 0; - jack_event_flag = 1; - } - if (intsts & 0x2) { - pr_debug("headphone detected\n"); - /* send headphone detect/undetect */ - jack = &intelmaddata->jack[0].jack; - present = (value == 0x2) ? 1 : 0; - jack_event_flag = 1; - } - if (intsts & 0x4) { - pr_debug("short push detected\n"); - /* send short push */ - jack = &intelmaddata->jack[2].jack; - present = 1; - jack_event_flag = 1; - buttonpressflag = 1; - } - if (intsts & 0x8) { - pr_debug("long push detected\n"); - /* send long push */ - jack = &intelmaddata->jack[3].jack; - present = 1; - jack_event_flag = 1; - buttonpressflag = 1; - } - - if (jack_event_flag) - sst_mad_send_jack_report(jack, buttonpressflag, present); - - -} - -void sst_process_mad_jack_detection(struct work_struct *work) -{ - u8 intsts; - struct mad_jack_msg_wq *mad_jack_detect = - container_of(work, struct mad_jack_msg_wq, wq); - - struct snd_intelmad *intelmaddata = - mad_jack_detect->intelmaddata; - - intsts = mad_jack_detect->intsts; - - switch (intelmaddata->sstdrv_ops->vendor_id) { - case SND_FS: - sst_mad_jackdetection_fs(intsts , intelmaddata); - break; - case SND_MX: - sst_mad_jackdetection_mx(intsts , intelmaddata); - break; - case SND_NC: - sst_mad_jackdetection_nec(intsts , intelmaddata); - break; - } } - static int __devinit snd_intelmad_register_irq( - struct snd_intelmad *intelmaddata) + struct snd_intelmad *intelmaddata, unsigned int regbase, + unsigned int regsize) { int ret_val; - u32 regbase = AUDINT_BASE, regsize = 8; char *drv_name; - pr_debug("irq reg done, regbase 0x%x, regsize 0x%x\n", + pr_debug("irq reg regbase 0x%x, regsize 0x%x\n", regbase, regsize); intelmaddata->int_base = ioremap_nocache(regbase, regsize); if (!intelmaddata->int_base) @@ -797,6 +603,7 @@ static int __devinit snd_intelmad_sst_register( intelmaddata->sstdrv_ops->scard_ops->input_dev_id = DMIC; intelmaddata->sstdrv_ops->scard_ops->output_dev_id = STEREO_HEADPHONE; + intelmaddata->sstdrv_ops->scard_ops->lineout_dev_id = NONE; } /* registering with SST driver to get access to SST APIs to use */ @@ -805,13 +612,15 @@ static int __devinit snd_intelmad_sst_register( pr_err("sst card registration failed\n"); return ret_val; } - sst_drv_ctx->scard_ops->card_status = SND_CARD_UN_INIT; - sst_card_vendor_id = intelmaddata->sstdrv_ops->vendor_id; intelmaddata->pmic_status = PMIC_UNINIT; return ret_val; } +static void snd_intelmad_page_free(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} /* Driver Init/exit functionalities */ /** * snd_intelmad_pcm_new - to setup pcm for the card @@ -863,6 +672,7 @@ static int __devinit snd_intelmad_pcm_new(struct snd_card *card, snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, cap_ops); /* setup private data which can be retrieved when required */ pcm->private_data = intelmaddata; + pcm->private_free = snd_intelmad_page_free; pcm->info_flags = 0; strncpy(pcm->name, card->shortname, strlen(card->shortname)); /* allocate dma pages for ALSA stream operations */ @@ -907,8 +717,12 @@ static int snd_intelmad_jack(struct snd_intelmad *intelmaddata) pr_debug("snd_intelmad_jack called\n"); jack = &intelmaddata->jack[0].jack; - retval = snd_jack_new(intelmaddata->card, "Headphone", - SND_JACK_HEADPHONE, &jack); + snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PHONE); + retval = snd_jack_new(intelmaddata->card, "Intel(R) MID Audio Jack", + SND_JACK_HEADPHONE | SND_JACK_HEADSET | + SW_JACK_PHYSICAL_INSERT | SND_JACK_BTN_0 + | SND_JACK_BTN_1, &jack); + pr_debug("snd_intelmad_jack called\n"); if (retval < 0) return retval; snd_jack_report(jack, 0); @@ -916,40 +730,6 @@ static int snd_intelmad_jack(struct snd_intelmad *intelmaddata) jack->private_data = jack; intelmaddata->jack[0].jack = *jack; - - jack = &intelmaddata->jack[1].jack; - retval = snd_jack_new(intelmaddata->card, "Headset", - SND_JACK_HEADSET, &jack); - if (retval < 0) - return retval; - - - - jack->private_data = jack; - intelmaddata->jack[1].jack = *jack; - - - jack = &intelmaddata->jack[2].jack; - retval = snd_jack_new(intelmaddata->card, "Short Press", - SND_JACK_HS_SHORT_PRESS, &jack); - if (retval < 0) - return retval; - - - jack->private_data = jack; - intelmaddata->jack[2].jack = *jack; - - - jack = &intelmaddata->jack[3].jack; - retval = snd_jack_new(intelmaddata->card, "Long Press", - SND_JACK_HS_LONG_PRESS, &jack); - if (retval < 0) - return retval; - - - jack->private_data = jack; - intelmaddata->jack[3].jack = *jack; - return retval; } @@ -1002,14 +782,14 @@ static int snd_intelmad_dev_free(struct snd_device *device) intelmaddata = device->device_data; pr_debug("snd_intelmad_dev_free called\n"); - snd_card_free(intelmaddata->card); - /*genl_unregister_family(&audio_event_genl_family);*/ unregister_sst_card(intelmaddata->sstdrv_ops); /* free allocated memory for internal context */ destroy_workqueue(intelmaddata->mad_jack_wq); + device->device_data = NULL; kfree(intelmaddata->sstdrv_ops); kfree(intelmaddata); + return 0; } @@ -1040,9 +820,10 @@ int __devinit snd_intelmad_probe(struct platform_device *pdev) int ret_val; struct snd_intelmad *intelmaddata; const struct platform_device_id *id = platform_get_device_id(pdev); - unsigned int cpu_id = (unsigned int)id->driver_data; + struct snd_intelmad_probe_info *info = (void *)id->driver_data; - pr_debug("probe for %s cpu_id %d\n", pdev->name, cpu_id); + pr_debug("probe for %s cpu_id %d\n", pdev->name, info->cpu_id); + pr_debug("rq_chache %x of size %x\n", info->irq_cache, info->size); if (!strcmp(pdev->name, DRIVER_NAME_MRST)) pr_debug("detected MRST\n"); else if (!strcmp(pdev->name, DRIVER_NAME_MFLD)) @@ -1051,7 +832,8 @@ int __devinit snd_intelmad_probe(struct platform_device *pdev) pr_err("detected unknown device abort!!\n"); return -EIO; } - if ((cpu_id < CPU_CHIP_LINCROFT) || (cpu_id > CPU_CHIP_PENWELL)) { + if ((info->cpu_id < CPU_CHIP_LINCROFT) || + (info->cpu_id > CPU_CHIP_PENWELL)) { pr_err("detected unknown cpu_id abort!!\n"); return -EIO; } @@ -1061,6 +843,7 @@ int __devinit snd_intelmad_probe(struct platform_device *pdev) pr_debug("mem alloctn fail\n"); return -ENOMEM; } + intelmad_drv = intelmaddata; /* allocate memory for LPE API set */ intelmaddata->sstdrv_ops = kzalloc(sizeof(struct intel_sst_card_ops), @@ -1071,7 +854,7 @@ int __devinit snd_intelmad_probe(struct platform_device *pdev) return -ENOMEM; } - intelmaddata->cpu_id = cpu_id; + intelmaddata->cpu_id = info->cpu_id; /* create a card instance with ALSA framework */ ret_val = snd_card_create(card_index, card_id, THIS_MODULE, 0, &card); if (ret_val) { @@ -1095,7 +878,7 @@ int __devinit snd_intelmad_probe(struct platform_device *pdev) ret_val = snd_intelmad_sst_register(intelmaddata); if (ret_val) { pr_err("snd_intelmad_sst_register failed\n"); - goto free_allocs; + goto set_null_data; } intelmaddata->pmic_status = PMIC_INIT; @@ -1103,20 +886,21 @@ int __devinit snd_intelmad_probe(struct platform_device *pdev) ret_val = snd_intelmad_pcm(card, intelmaddata); if (ret_val) { pr_err("snd_intelmad_pcm failed\n"); - goto free_allocs; + goto free_sst; } ret_val = snd_intelmad_mixer(intelmaddata); if (ret_val) { pr_err("snd_intelmad_mixer failed\n"); - goto free_allocs; + goto free_card; } ret_val = snd_intelmad_jack(intelmaddata); if (ret_val) { pr_err("snd_intelmad_jack failed\n"); - goto free_allocs; + goto free_card; } + intelmaddata->adc_address = mid_initialize_adc(); /*create work queue for jack interrupt*/ INIT_WORK(&intelmaddata->mad_jack_msg.wq, @@ -1124,33 +908,48 @@ int __devinit snd_intelmad_probe(struct platform_device *pdev) intelmaddata->mad_jack_wq = create_workqueue("sst_mad_jack_wq"); if (!intelmaddata->mad_jack_wq) - goto free_mad_jack_wq; + goto free_card; - ret_val = snd_intelmad_register_irq(intelmaddata); + ret_val = snd_intelmad_register_irq(intelmaddata, + info->irq_cache, info->size); if (ret_val) { pr_err("snd_intelmad_register_irq fail\n"); - goto free_allocs; + goto free_mad_jack_wq; } /* internal function call to register device with ALSA */ ret_val = snd_intelmad_create(intelmaddata, card); if (ret_val) { pr_err("snd_intelmad_create failed\n"); - goto free_allocs; + goto set_pvt_data; } card->private_data = &intelmaddata; snd_card_set_dev(card, &pdev->dev); ret_val = snd_card_register(card); if (ret_val) { pr_err("snd_card_register failed\n"); - goto free_allocs; + goto set_pvt_data; + } + if (pdev->dev.platform_data) { + int gpio_amp = *(int *)pdev->dev.platform_data; + if (gpio_request_one(gpio_amp, GPIOF_OUT_INIT_LOW, "amp power")) + gpio_amp = 0; + intelmaddata->sstdrv_ops->scard_ops->gpio_amp = gpio_amp; } pr_debug("snd_intelmad_probe complete\n"); return ret_val; +set_pvt_data: + card->private_data = NULL; free_mad_jack_wq: destroy_workqueue(intelmaddata->mad_jack_wq); +free_card: + snd_card_free(intelmaddata->card); +free_sst: + unregister_sst_card(intelmaddata->sstdrv_ops); +set_null_data: + platform_set_drvdata(pdev, NULL); free_allocs: pr_err("probe failed\n"); snd_card_free(card); @@ -1165,13 +964,13 @@ static int snd_intelmad_remove(struct platform_device *pdev) struct snd_intelmad *intelmaddata = platform_get_drvdata(pdev); if (intelmaddata) { + if (intelmaddata->sstdrv_ops->scard_ops->gpio_amp) + gpio_free(intelmaddata->sstdrv_ops->scard_ops->gpio_amp); + free_irq(intelmaddata->irq, intelmaddata); snd_card_free(intelmaddata->card); - unregister_sst_card(intelmaddata->sstdrv_ops); - /* free allocated memory for internal context */ - destroy_workqueue(intelmaddata->mad_jack_wq); - kfree(intelmaddata->sstdrv_ops); - kfree(intelmaddata); } + intelmad_drv = NULL; + platform_set_drvdata(pdev, NULL); return 0; } @@ -1179,8 +978,8 @@ static int snd_intelmad_remove(struct platform_device *pdev) * Driver initialization and exit *********************************************************************/ static const struct platform_device_id snd_intelmad_ids[] = { - {DRIVER_NAME_MRST, CPU_CHIP_LINCROFT}, - {DRIVER_NAME_MFLD, CPU_CHIP_PENWELL}, + {DRIVER_NAME_MRST, INFO(CPU_CHIP_LINCROFT, AUDINT_BASE, 1)}, + {DRIVER_NAME_MFLD, INFO(CPU_CHIP_PENWELL, 0xFFFF7FCD, 1)}, {"", 0}, }; diff --git a/drivers/staging/intel_sst/intelmid.h b/drivers/staging/intel_sst/intelmid.h index e77da87e1df0..14a7ba078b7c 100644 --- a/drivers/staging/intel_sst/intelmid.h +++ b/drivers/staging/intel_sst/intelmid.h @@ -28,6 +28,7 @@ #define __INTELMID_H #include <linux/time.h> +#include <sound/jack.h> #define DRIVER_NAME_MFLD "msic_audio" #define DRIVER_NAME_MRST "pmic_audio" @@ -43,7 +44,7 @@ #define MAX_BUFFER (800*1024) /* for PCM */ #define MIN_BUFFER (800*1024) #define MAX_PERIODS (1024*2) -#define MIN_PERIODS 1 +#define MIN_PERIODS 2 #define MAX_PERIOD_BYTES MAX_BUFFER #define MIN_PERIOD_BYTES 32 /*#define MIN_PERIOD_BYTES 160*/ @@ -53,12 +54,12 @@ #define STEREO_CNTL 2 #define MIN_CHANNEL 1 #define MAX_CHANNEL_AMIC 2 -#define MAX_CHANNEL_DMIC 4 +#define MAX_CHANNEL_DMIC 5 #define FIFO_SIZE 0 /* fifo not being used */ #define INTEL_MAD "Intel MAD" -#define MAX_CTRL_MRST 7 -#define MAX_CTRL_MFLD 2 -#define MAX_CTRL 7 +#define MAX_CTRL_MRST 8 +#define MAX_CTRL_MFLD 7 +#define MAX_CTRL 8 #define MAX_VENDORS 4 /* TODO +6 db */ #define MAX_VOL 64 @@ -66,12 +67,17 @@ #define MIN_VOL 0 #define PLAYBACK_COUNT 1 #define CAPTURE_COUNT 1 +#define ADC_ONE_LSB_MULTIPLIER 2346 + +#define MID_JACK_HS_LONG_PRESS SND_JACK_BTN_0 +#define MID_JACK_HS_SHORT_PRESS SND_JACK_BTN_1 extern int sst_card_vendor_id; struct mad_jack { struct snd_jack jack; int jack_status; + int jack_dev_state; struct timeval buttonpressed; struct timeval buttonreleased; }; @@ -83,6 +89,12 @@ struct mad_jack_msg_wq { }; +struct snd_intelmad_probe_info { + unsigned int cpu_id; + unsigned int irq_cache; + unsigned int size; +}; + /** * struct snd_intelmad - intelmad driver structure * @@ -116,10 +128,12 @@ struct snd_intelmad { void __iomem *int_base; int output_sel; int input_sel; + int lineout_sel; int master_mute; struct mad_jack jack[4]; int playback_cnt; int capture_cnt; + u16 adc_address; struct mad_jack_msg_wq mad_jack_msg; struct workqueue_struct *mad_jack_wq; u8 jack_prev_state; @@ -131,6 +145,8 @@ struct snd_control_val { int playback_vol_min; int capture_vol_max; int capture_vol_min; + int master_vol_max; + int master_vol_min; }; struct mad_stream_pvt { @@ -161,8 +177,18 @@ enum _widget_ctrl { PLAYBACK_MUTE, CAPTURE_VOL, CAPTURE_MUTE, + MASTER_VOL, MASTER_MUTE }; +enum _widget_ctrl_mfld { + LINEOUT_SEL_MFLD = 3, +}; +enum hw_chs { + HW_CH0 = 0, + HW_CH1, + HW_CH2, + HW_CH3 +}; void period_elapsed(void *mad_substream); int snd_intelmad_alloc_stream(struct snd_pcm_substream *substream); @@ -177,5 +203,7 @@ extern struct snd_control_val intelmad_ctrl_val[]; extern struct snd_kcontrol_new snd_intelmad_controls_mrst[]; extern struct snd_kcontrol_new snd_intelmad_controls_mfld[]; extern struct snd_pmic_ops *intelmad_vendor_ops[]; +void sst_mad_send_jack_report(struct snd_jack *jack, + int buttonpressevent , int status); #endif /* __INTELMID_H */ diff --git a/drivers/staging/intel_sst/intelmid_adc_control.h b/drivers/staging/intel_sst/intelmid_adc_control.h new file mode 100644 index 000000000000..65d5c3988762 --- /dev/null +++ b/drivers/staging/intel_sst/intelmid_adc_control.h @@ -0,0 +1,193 @@ +#ifndef __INTELMID_ADC_CONTROL_H__ +#define __INTELMID_ADC_CONTROL_H_ +/* + * intelmid_adc_control.h - Intel SST Driver for audio engine + * + * Copyright (C) 2008-10 Intel Corporation + * Authors: R Durgadadoss <r.durgadoss@intel.com> + * Dharageswari R <dharageswari.r@intel.com> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Common private ADC declarations for SST + */ + + +#define MSIC_ADC1CNTL1 0x1C0 +#define MSIC_ADC_ENBL 0x10 +#define MSIC_ADC_START 0x08 + +#define MSIC_ADC1CNTL3 0x1C2 +#define MSIC_ADCTHERM_ENBL 0x04 +#define MSIC_ADCRRDATA_ENBL 0x05 + +#define MSIC_STOPBIT_MASK 16 +#define MSIC_ADCTHERM_MASK 4 + +#define ADC_CHANLS_MAX 15 /* Number of ADC channels */ +#define ADC_LOOP_MAX (ADC_CHANLS_MAX - 1) + +/* ADC channel code values */ +#define AUDIO_DETECT_CODE 0x06 + +/* ADC base addresses */ +#define ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */ +#define ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */ + + +/** + * configure_adc - enables/disables the ADC for conversion + * @val: zero: disables the ADC non-zero:enables the ADC + * + * Enable/Disable the ADC depending on the argument + * + * Can sleep + */ +static inline int configure_adc(int val) +{ + int ret; + struct sc_reg_access sc_access = {0,}; + + + sc_access.reg_addr = MSIC_ADC1CNTL1; + ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1); + if (ret) + return ret; + + if (val) + /* Enable and start the ADC */ + sc_access.value |= (MSIC_ADC_ENBL | MSIC_ADC_START); + else + /* Just stop the ADC */ + sc_access.value &= (~MSIC_ADC_START); + sc_access.reg_addr = MSIC_ADC1CNTL1; + return sst_sc_reg_access(&sc_access, PMIC_WRITE, 1); +} + +/** + * reset_stopbit - sets the stop bit to 0 on the given channel + * @addr: address of the channel + * + * Can sleep + */ +static inline int reset_stopbit(uint16_t addr) +{ + int ret; + struct sc_reg_access sc_access = {0,}; + sc_access.reg_addr = addr; + ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1); + if (ret) + return ret; + /* Set the stop bit to zero */ + sc_access.reg_addr = addr; + sc_access.value = (sc_access.value) & 0xEF; + return sst_sc_reg_access(&sc_access, PMIC_WRITE, 1); +} + +/** + * find_free_channel - finds an empty channel for conversion + * + * If the ADC is not enabled then start using 0th channel + * itself. Otherwise find an empty channel by looking for a + * channel in which the stopbit is set to 1. returns the index + * of the first free channel if succeeds or an error code. + * + * Context: can sleep + * + */ +static inline int find_free_channel(void) +{ + int ret; + int i; + + struct sc_reg_access sc_access = {0,}; + + /* check whether ADC is enabled */ + sc_access.reg_addr = MSIC_ADC1CNTL1; + ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1); + if (ret) + return ret; + + if ((sc_access.value & MSIC_ADC_ENBL) == 0) + return 0; + + /* ADC is already enabled; Looking for an empty channel */ + for (i = 0; i < ADC_CHANLS_MAX; i++) { + + sc_access.reg_addr = ADC_CHNL_START_ADDR + i; + ret = sst_sc_reg_access(&sc_access, PMIC_READ, 1); + if (ret) + return ret; + + if (sc_access.value & MSIC_STOPBIT_MASK) { + ret = i; + break; + } + } + return (ret > ADC_LOOP_MAX) ? (-EINVAL) : ret; +} + +/** + * mid_initialize_adc - initializing the ADC + * @dev: our device structure + * + * Initialize the ADC for reading thermistor values. Can sleep. + */ +static inline int mid_initialize_adc(void) +{ + int base_addr, chnl_addr; + int ret; + static int channel_index; + struct sc_reg_access sc_access = {0,}; + + /* Index of the first channel in which the stop bit is set */ + channel_index = find_free_channel(); + if (channel_index < 0) { + pr_err("No free ADC channels"); + return channel_index; + } + + base_addr = ADC_CHNL_START_ADDR + channel_index; + + if (!(channel_index == 0 || channel_index == ADC_LOOP_MAX)) { + /* Reset stop bit for channels other than 0 and 12 */ + ret = reset_stopbit(base_addr); + if (ret) + return ret; + + /* Index of the first free channel */ + base_addr++; + channel_index++; + } + + /* Since this is the last channel, set the stop bit + to 1 by ORing the DIE_SENSOR_CODE with 0x10 */ + sc_access.reg_addr = base_addr; + sc_access.value = AUDIO_DETECT_CODE | 0x10; + ret = sst_sc_reg_access(&sc_access, PMIC_WRITE, 1); + if (ret) { + pr_err("unable to enable ADC"); + return ret; + } + + chnl_addr = ADC_DATA_START_ADDR + 2 * channel_index; + pr_debug("mid_initialize : %x", chnl_addr); + configure_adc(1); + return chnl_addr; +} +#endif + diff --git a/drivers/staging/intel_sst/intelmid_ctrl.c b/drivers/staging/intel_sst/intelmid_ctrl.c index 69af0704ce94..19ec474b362a 100644 --- a/drivers/staging/intel_sst/intelmid_ctrl.c +++ b/drivers/staging/intel_sst/intelmid_ctrl.c @@ -29,17 +29,37 @@ #include <sound/core.h> #include <sound/control.h> -#include "jack.h" #include "intel_sst.h" #include "intel_sst_ioctl.h" #include "intelmid_snd_control.h" #include "intelmid.h" +#define HW_CH_BASE 4 + + +#define HW_CH_0 "Hw1" +#define HW_CH_1 "Hw2" +#define HW_CH_2 "Hw3" +#define HW_CH_3 "Hw4" + +static char *router_dmics[] = { "DMIC1", + "DMIC2", + "DMIC3", + "DMIC4", + "DMIC5", + "DMIC6" + }; + static char *out_names_mrst[] = {"Headphones", "Internal speakers"}; static char *in_names_mrst[] = {"AMIC", "DMIC", "HS_MIC"}; +static char *line_out_names_mfld[] = {"Headset", + "IHF ", + "Vibra1 ", + "Vibra2 ", + "NONE "}; static char *out_names_mfld[] = {"Headset ", "EarPiece "}; static char *in_names_mfld[] = {"AMIC", @@ -60,9 +80,11 @@ struct snd_control_val intelmad_ctrl_val[MAX_VENDORS] = { }, { .playback_vol_max = 0, - .playback_vol_min = -126, + .playback_vol_min = -31, .capture_vol_max = 0, .capture_vol_min = -31, + .master_vol_max = 0, + .master_vol_min = -126, }, }; @@ -139,6 +161,15 @@ static int snd_intelmad_playback_volume_info(struct snd_kcontrol *kcontrol, return 0; } +static int snd_intelmad_master_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + snd_intelmad_volume_info(uinfo, STEREO_CNTL, + intelmad_ctrl_val[sst_card_vendor_id].master_vol_max, + intelmad_ctrl_val[sst_card_vendor_id].master_vol_min); + return 0; +} + /** * snd_intelmad_device_info_mrst - provides information about the devices available * @@ -179,13 +210,27 @@ static int snd_intelmad_device_info_mrst(struct snd_kcontrol *kcontrol, static int snd_intelmad_device_info_mfld(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { + struct snd_pmic_ops *scard_ops; + struct snd_intelmad *intelmaddata; + WARN_ON(!kcontrol); WARN_ON(!uinfo); + + intelmaddata = kcontrol->private_data; + + WARN_ON(!intelmaddata->sstdrv_ops); + + scard_ops = intelmaddata->sstdrv_ops->scard_ops; /* setup device select as drop down controls with different values */ if (kcontrol->id.numid == OUTPUT_SEL) uinfo->value.enumerated.items = ARRAY_SIZE(out_names_mfld); - else + else if (kcontrol->id.numid == INPUT_SEL) uinfo->value.enumerated.items = ARRAY_SIZE(in_names_mfld); + else if (kcontrol->id.numid == LINEOUT_SEL_MFLD) { + uinfo->value.enumerated.items = ARRAY_SIZE(line_out_names_mfld); + scard_ops->line_out_names_cnt = uinfo->value.enumerated.items; + } else + return -EINVAL; uinfo->count = MONO_CNTL; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; @@ -195,10 +240,16 @@ static int snd_intelmad_device_info_mfld(struct snd_kcontrol *kcontrol, strncpy(uinfo->value.enumerated.name, out_names_mfld[uinfo->value.enumerated.item], sizeof(uinfo->value.enumerated.name)-1); - else + else if (kcontrol->id.numid == INPUT_SEL) strncpy(uinfo->value.enumerated.name, in_names_mfld[uinfo->value.enumerated.item], sizeof(uinfo->value.enumerated.name)-1); + else if (kcontrol->id.numid == LINEOUT_SEL_MFLD) + strncpy(uinfo->value.enumerated.name, + line_out_names_mfld[uinfo->value.enumerated.item], + sizeof(uinfo->value.enumerated.name)-1); + else + return -EINVAL; return 0; } @@ -241,6 +292,11 @@ static int snd_intelmad_volume_get(struct snd_kcontrol *kcontrol, case CAPTURE_VOL: cntl_list[0] = PMIC_SND_CAPTURE_VOL; break; + + case MASTER_VOL: + cntl_list[0] = PMIC_SND_RIGHT_MASTER_VOL; + cntl_list[1] = PMIC_SND_LEFT_MASTER_VOL; + break; default: return -EINVAL; } @@ -251,7 +307,8 @@ static int snd_intelmad_volume_get(struct snd_kcontrol *kcontrol, if (ret_val) return ret_val; - if (kcontrol->id.numid == PLAYBACK_VOL) { + if (kcontrol->id.numid == PLAYBACK_VOL || + kcontrol->id.numid == MASTER_VOL) { ret_val = scard_ops->get_vol(cntl_list[1], &value); uval->value.integer.value[1] = value; } @@ -359,6 +416,12 @@ static int snd_intelmad_volume_set(struct snd_kcontrol *kcontrol, case CAPTURE_VOL: cntl_list[0] = PMIC_SND_CAPTURE_VOL; break; + + case MASTER_VOL: + cntl_list[0] = PMIC_SND_LEFT_MASTER_VOL; + cntl_list[1] = PMIC_SND_RIGHT_MASTER_VOL; + break; + default: return -EINVAL; } @@ -368,7 +431,8 @@ static int snd_intelmad_volume_set(struct snd_kcontrol *kcontrol, if (ret_val) return ret_val; - if (kcontrol->id.numid == PLAYBACK_VOL) + if (kcontrol->id.numid == PLAYBACK_VOL || + kcontrol->id.numid == MASTER_VOL) ret_val = scard_ops->set_vol(cntl_list[1], uval->value.integer.value[1]); return ret_val; @@ -464,14 +528,36 @@ static int snd_intelmad_device_get(struct snd_kcontrol *kcontrol, WARN_ON(!kcontrol); intelmaddata = kcontrol->private_data; + scard_ops = intelmaddata->sstdrv_ops->scard_ops; if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) { - scard_ops = intelmaddata->sstdrv_ops->scard_ops; if (kcontrol->id.numid == OUTPUT_SEL) uval->value.enumerated.item[0] = scard_ops->output_dev_id; else if (kcontrol->id.numid == INPUT_SEL) uval->value.enumerated.item[0] = scard_ops->input_dev_id; + else if (kcontrol->id.numid == LINEOUT_SEL_MFLD) + uval->value.enumerated.item[0] = + scard_ops->lineout_dev_id; + else + return -EINVAL; + } else if (intelmaddata->cpu_id == CPU_CHIP_LINCROFT) { + if (kcontrol->id.numid == OUTPUT_SEL) + /* There is a mismatch here. + * ALSA expects 1 for internal speaker. + * But internally, we may give 2 for internal speaker. + */ + if (scard_ops->output_dev_id == MONO_EARPIECE || + scard_ops->output_dev_id == INTERNAL_SPKR) + uval->value.enumerated.item[0] = MONO_EARPIECE; + else if (scard_ops->output_dev_id == STEREO_HEADPHONE) + uval->value.enumerated.item[0] = + STEREO_HEADPHONE; + else + return -EINVAL; + else if (kcontrol->id.numid == INPUT_SEL) + uval->value.enumerated.item[0] = + scard_ops->input_dev_id; else return -EINVAL; } else @@ -534,6 +620,11 @@ static int snd_intelmad_device_set(struct snd_kcontrol *kcontrol, uval->value.enumerated.item[0]); intelmaddata->input_sel = uval->value.enumerated.item[0]; break; + case LINEOUT_SEL_MFLD: + ret_val = scard_ops->set_lineout_dev( + uval->value.enumerated.item[0]); + intelmaddata->lineout_sel = uval->value.enumerated.item[0]; + break; default: return -EINVAL; } @@ -541,6 +632,151 @@ static int snd_intelmad_device_set(struct snd_kcontrol *kcontrol, return ret_val; } +static int snd_intelmad_device_dmic_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uval) +{ + struct snd_intelmad *intelmaddata; + struct snd_pmic_ops *scard_ops; + + WARN_ON(!uval); + WARN_ON(!kcontrol); + + intelmaddata = kcontrol->private_data; + scard_ops = intelmaddata->sstdrv_ops->scard_ops; + + if (scard_ops->input_dev_id != DMIC) { + pr_debug("input dev = 0x%x\n", scard_ops->input_dev_id); + return 0; + } + + if (intelmaddata->cpu_id == CPU_CHIP_PENWELL) + uval->value.enumerated.item[0] = kcontrol->private_value; + else + pr_debug(" CPU id = 0x%xis invalid.\n", + intelmaddata->cpu_id); + return 0; +} + +void msic_set_bit(u8 index, unsigned int *available_dmics) +{ + *available_dmics |= (1 << index); +} + +void msic_clear_bit(u8 index, unsigned int *available_dmics) +{ + *available_dmics &= ~(1 << index); +} + +int msic_is_set_bit(u8 index, unsigned int *available_dmics) +{ + int ret_val; + + ret_val = (*available_dmics & (1 << index)); + return ret_val; +} + +static int snd_intelmad_device_dmic_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uval) +{ + struct snd_intelmad *intelmaddata; + struct snd_pmic_ops *scard_ops; + int i, dmic_index; + unsigned int available_dmics; + int jump_count; + int max_dmics = ARRAY_SIZE(router_dmics); + + WARN_ON(!uval); + WARN_ON(!kcontrol); + + intelmaddata = kcontrol->private_data; + WARN_ON(!intelmaddata->sstdrv_ops); + + scard_ops = intelmaddata->sstdrv_ops->scard_ops; + WARN_ON(!scard_ops); + + if (scard_ops->input_dev_id != DMIC) { + pr_debug("input dev = 0x%x\n", scard_ops->input_dev_id); + return 0; + } + + available_dmics = scard_ops->available_dmics; + + if (kcontrol->private_value > uval->value.enumerated.item[0]) { + pr_debug("jump count -1.\n"); + jump_count = -1; + } else { + pr_debug("jump count 1.\n"); + jump_count = 1; + } + + dmic_index = uval->value.enumerated.item[0]; + pr_debug("set function. dmic_index = %d, avl_dmic = 0x%x\n", + dmic_index, available_dmics); + for (i = 0; i < max_dmics; i++) { + pr_debug("set function. loop index = 0x%x. dmic_index = 0x%x\n", + i, dmic_index); + if (!msic_is_set_bit(dmic_index, &available_dmics)) { + msic_clear_bit(kcontrol->private_value, + &available_dmics); + msic_set_bit(dmic_index, &available_dmics); + kcontrol->private_value = dmic_index; + scard_ops->available_dmics = available_dmics; + scard_ops->hw_dmic_map[kcontrol->id.numid-HW_CH_BASE] = + kcontrol->private_value; + scard_ops->set_hw_dmic_route + (kcontrol->id.numid-HW_CH_BASE); + return 0; + } + + dmic_index += jump_count; + + if (dmic_index > (max_dmics - 1) && jump_count == 1) { + pr_debug("Resettingthe dmic index to 0.\n"); + dmic_index = 0; + } else if (dmic_index == -1 && jump_count == -1) { + pr_debug("Resetting the dmic index to 5.\n"); + dmic_index = max_dmics - 1; + } + } + + return -EINVAL; +} + +static int snd_intelmad_device_dmic_info_mfld(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_intelmad *intelmaddata; + struct snd_pmic_ops *scard_ops; + + uinfo->count = MONO_CNTL; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->value.enumerated.items = ARRAY_SIZE(router_dmics); + + intelmaddata = kcontrol->private_data; + WARN_ON(!intelmaddata->sstdrv_ops); + + scard_ops = intelmaddata->sstdrv_ops->scard_ops; + WARN_ON(!scard_ops); + + if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) + uinfo->value.enumerated.item = + uinfo->value.enumerated.items - 1; + + strncpy(uinfo->value.enumerated.name, + router_dmics[uinfo->value.enumerated.item], + sizeof(uinfo->value.enumerated.name)-1); + + + msic_set_bit(kcontrol->private_value, &scard_ops->available_dmics); + pr_debug("info function. avl_dmic = 0x%x", + scard_ops->available_dmics); + + scard_ops->hw_dmic_map[kcontrol->id.numid-HW_CH_BASE] = + kcontrol->private_value; + + return 0; +} + struct snd_kcontrol_new snd_intelmad_controls_mrst[MAX_CTRL] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -598,6 +834,15 @@ struct snd_kcontrol_new snd_intelmad_controls_mrst[MAX_CTRL] __devinitdata = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_intelmad_master_volume_info, + .get = snd_intelmad_volume_get, + .put = snd_intelmad_volume_set, + .private_value = 0, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Master Playback Switch", .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .info = snd_intelmad_mute_info, @@ -627,5 +872,50 @@ snd_intelmad_controls_mfld[MAX_CTRL_MFLD] __devinitdata = { .put = snd_intelmad_device_set, .private_value = 0, }, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line out", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_intelmad_device_info_mfld, + .get = snd_intelmad_device_get, + .put = snd_intelmad_device_set, + .private_value = 0, +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = HW_CH_0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_intelmad_device_dmic_info_mfld, + .get = snd_intelmad_device_dmic_get, + .put = snd_intelmad_device_dmic_set, + .private_value = 0 +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = HW_CH_1, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_intelmad_device_dmic_info_mfld, + .get = snd_intelmad_device_dmic_get, + .put = snd_intelmad_device_dmic_set, + .private_value = 1 +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = HW_CH_2, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_intelmad_device_dmic_info_mfld, + .get = snd_intelmad_device_dmic_get, + .put = snd_intelmad_device_dmic_set, + .private_value = 2 +}, +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = HW_CH_3, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_intelmad_device_dmic_info_mfld, + .get = snd_intelmad_device_dmic_get, + .put = snd_intelmad_device_dmic_set, + .private_value = 3 +} }; diff --git a/drivers/staging/intel_sst/intelmid_msic_control.c b/drivers/staging/intel_sst/intelmid_msic_control.c index da093ed0cd88..70cdb1697815 100644 --- a/drivers/staging/intel_sst/intelmid_msic_control.c +++ b/drivers/staging/intel_sst/intelmid_msic_control.c @@ -28,9 +28,15 @@ #include <linux/pci.h> #include <linux/file.h> +#include <linux/delay.h> +#include <sound/control.h> #include "intel_sst.h" -#include "intel_sst_ioctl.h" +#include <linux/input.h> #include "intelmid_snd_control.h" +#include "intelmid.h" + +#define AUDIOMUX12 0x24c +#define AUDIOMUX34 0x24d static int msic_init_card(void) { @@ -54,116 +60,359 @@ static int msic_init_card(void) /*TI vibra w/a settings*/ {0x384, 0x80, 0}, {0x385, 0x80, 0}, - /*vibra settings*/ {0x267, 0x00, 0}, - {0x26A, 0x10, 0}, {0x261, 0x00, 0}, - {0x264, 0x10, 0}, /* pcm port setting */ {0x278, 0x00, 0}, {0x27B, 0x01, 0}, {0x27C, 0x0a, 0}, /* Set vol HSLRVOLCTRL, IHFVOL */ - {0x259, 0x04, 0}, - {0x25A, 0x04, 0}, - {0x25B, 0x04, 0}, - {0x25C, 0x04, 0}, + {0x259, 0x08, 0}, + {0x25A, 0x08, 0}, + {0x25B, 0x08, 0}, + {0x25C, 0x08, 0}, /* HSEPRXCTRL Enable the headset left and right FIR filters */ {0x250, 0x30, 0}, /* HSMIXER */ {0x256, 0x11, 0}, /* amic configuration */ - {0x249, 0x09, 0x0}, - {0x24A, 0x09, 0x0}, + {0x249, 0x01, 0x0}, + {0x24A, 0x01, 0x0}, /* unmask ocaudio/accdet interrupts */ {0x1d, 0x00, 0x00}, {0x1e, 0x00, 0x00}, }; snd_msic_ops.card_status = SND_CARD_INIT_DONE; - sst_sc_reg_access(sc_access, PMIC_WRITE, 30); + sst_sc_reg_access(sc_access, PMIC_WRITE, 28); snd_msic_ops.pb_on = 0; + snd_msic_ops.pbhs_on = 0; snd_msic_ops.cap_on = 0; snd_msic_ops.input_dev_id = DMIC; /*def dev*/ snd_msic_ops.output_dev_id = STEREO_HEADPHONE; + snd_msic_ops.jack_interrupt_status = false; pr_debug("msic init complete!!\n"); return 0; } +static int msic_line_out_restore(u8 value) +{ + struct sc_reg_access hs_drv_en[] = { + {0x25d, 0x03, 0x03}, + }; + struct sc_reg_access ep_drv_en[] = { + {0x25d, 0x40, 0x40}, + }; + struct sc_reg_access ihf_drv_en[] = { + {0x25d, 0x0c, 0x0c}, + }; + struct sc_reg_access vib1_drv_en[] = { + {0x25d, 0x10, 0x10}, + }; + struct sc_reg_access vib2_drv_en[] = { + {0x25d, 0x20, 0x20}, + }; + struct sc_reg_access pmode_enable[] = { + {0x381, 0x10, 0x10}, + }; + int retval = 0; + + pr_debug("msic_lineout_restore_lineout_dev:%d\n", value); + + switch (value) { + case HEADSET: + pr_debug("Selecting Lineout-HEADSET-restore\n"); + if (snd_msic_ops.output_dev_id == STEREO_HEADPHONE) + retval = sst_sc_reg_access(hs_drv_en, + PMIC_READ_MODIFY, 1); + else + retval = sst_sc_reg_access(ep_drv_en, + PMIC_READ_MODIFY, 1); + break; + case IHF: + pr_debug("Selecting Lineout-IHF-restore\n"); + retval = sst_sc_reg_access(ihf_drv_en, PMIC_READ_MODIFY, 1); + if (retval) + return retval; + retval = sst_sc_reg_access(pmode_enable, PMIC_READ_MODIFY, 1); + break; + case VIBRA1: + pr_debug("Selecting Lineout-Vibra1-restore\n"); + retval = sst_sc_reg_access(vib1_drv_en, PMIC_READ_MODIFY, 1); + break; + case VIBRA2: + pr_debug("Selecting Lineout-VIBRA2-restore\n"); + retval = sst_sc_reg_access(vib2_drv_en, PMIC_READ_MODIFY, 1); + break; + case NONE: + pr_debug("Selecting Lineout-NONE-restore\n"); + break; + default: + return -EINVAL; + } + return retval; +} +static int msic_get_lineout_prvstate(void) +{ + struct sc_reg_access hs_ihf_drv[2] = { + {0x257, 0x0, 0x0}, + {0x25d, 0x0, 0x0}, + }; + struct sc_reg_access vib1drv[2] = { + {0x264, 0x0, 0x0}, + {0x25D, 0x0, 0x0}, + }; + struct sc_reg_access vib2drv[2] = { + {0x26A, 0x0, 0x0}, + {0x25D, 0x0, 0x0}, + }; + int retval = 0, drv_en, dac_en, dev_id, mask; + for (dev_id = 0; dev_id < snd_msic_ops.line_out_names_cnt; dev_id++) { + switch (dev_id) { + case HEADSET: + pr_debug("msic_get_lineout_prvs_state: HEADSET\n"); + sst_sc_reg_access(hs_ihf_drv, PMIC_READ, 2); + + mask = (MASK0|MASK1); + dac_en = (hs_ihf_drv[0].value) & mask; + + mask = ((MASK0|MASK1)|MASK6); + drv_en = (hs_ihf_drv[1].value) & mask; + + if (dac_en && (!drv_en)) { + snd_msic_ops.prev_lineout_dev_id = HEADSET; + return retval; + } + break; + case IHF: + pr_debug("msic_get_lineout_prvstate: IHF\n"); + sst_sc_reg_access(hs_ihf_drv, PMIC_READ, 2); + + mask = (MASK2 | MASK3); + dac_en = (hs_ihf_drv[0].value) & mask; + + mask = (MASK2 | MASK3); + drv_en = (hs_ihf_drv[1].value) & mask; + + if (dac_en && (!drv_en)) { + snd_msic_ops.prev_lineout_dev_id = IHF; + return retval; + } + break; + case VIBRA1: + pr_debug("msic_get_lineout_prvstate: vibra1\n"); + sst_sc_reg_access(vib1drv, PMIC_READ, 2); + + mask = MASK1; + dac_en = (vib1drv[0].value) & mask; + + mask = MASK4; + drv_en = (vib1drv[1].value) & mask; + + if (dac_en && (!drv_en)) { + snd_msic_ops.prev_lineout_dev_id = VIBRA1; + return retval; + } + break; + case VIBRA2: + pr_debug("msic_get_lineout_prvstate: vibra2\n"); + sst_sc_reg_access(vib2drv, PMIC_READ, 2); + + mask = MASK1; + dac_en = (vib2drv[0].value) & mask; + + mask = MASK5; + drv_en = ((vib2drv[1].value) & mask); + + if (dac_en && (!drv_en)) { + snd_msic_ops.prev_lineout_dev_id = VIBRA2; + return retval; + } + break; + case NONE: + pr_debug("msic_get_lineout_prvstate: NONE\n"); + snd_msic_ops.prev_lineout_dev_id = NONE; + return retval; + default: + pr_debug("Invalid device id\n"); + snd_msic_ops.prev_lineout_dev_id = NONE; + return -EINVAL; + } + } + return retval; +} +static int msic_set_selected_lineout_dev(u8 value) +{ + struct sc_reg_access lout_hs[] = { + {0x25e, 0x33, 0xFF}, + {0x25d, 0x0, 0x43}, + }; + struct sc_reg_access lout_ihf[] = { + {0x25e, 0x55, 0xff}, + {0x25d, 0x0, 0x0c}, + }; + struct sc_reg_access lout_vibra1[] = { + + {0x25e, 0x61, 0xff}, + {0x25d, 0x0, 0x10}, + }; + struct sc_reg_access lout_vibra2[] = { + + {0x25e, 0x16, 0xff}, + {0x25d, 0x0, 0x20}, + }; + struct sc_reg_access lout_def[] = { + {0x25e, 0x66, 0x0}, + }; + struct sc_reg_access pmode_disable[] = { + {0x381, 0x00, 0x10}, + }; + struct sc_reg_access pmode_enable[] = { + {0x381, 0x10, 0x10}, + }; + int retval = 0; + + pr_debug("msic_set_selected_lineout_dev:%d\n", value); + msic_get_lineout_prvstate(); + msic_line_out_restore(snd_msic_ops.prev_lineout_dev_id); + snd_msic_ops.lineout_dev_id = value; + + switch (value) { + case HEADSET: + pr_debug("Selecting Lineout-HEADSET\n"); + if (snd_msic_ops.pb_on) + retval = sst_sc_reg_access(lout_hs, + PMIC_READ_MODIFY, 2); + if (retval) + return retval; + retval = sst_sc_reg_access(pmode_disable, + PMIC_READ_MODIFY, 1); + break; + case IHF: + pr_debug("Selecting Lineout-IHF\n"); + if (snd_msic_ops.pb_on) + retval = sst_sc_reg_access(lout_ihf, + PMIC_READ_MODIFY, 2); + if (retval) + return retval; + retval = sst_sc_reg_access(pmode_enable, + PMIC_READ_MODIFY, 1); + break; + case VIBRA1: + pr_debug("Selecting Lineout-Vibra1\n"); + if (snd_msic_ops.pb_on) + retval = sst_sc_reg_access(lout_vibra1, + PMIC_READ_MODIFY, 2); + if (retval) + return retval; + retval = sst_sc_reg_access(pmode_disable, + PMIC_READ_MODIFY, 1); + break; + case VIBRA2: + pr_debug("Selecting Lineout-VIBRA2\n"); + if (snd_msic_ops.pb_on) + retval = sst_sc_reg_access(lout_vibra2, + PMIC_READ_MODIFY, 2); + if (retval) + return retval; + retval = sst_sc_reg_access(pmode_disable, + PMIC_READ_MODIFY, 1); + break; + case NONE: + pr_debug("Selecting Lineout-NONE\n"); + retval = sst_sc_reg_access(lout_def, + PMIC_WRITE, 1); + if (retval) + return retval; + retval = sst_sc_reg_access(pmode_disable, + PMIC_READ_MODIFY, 1); + break; + default: + return -EINVAL; + } + return retval; +} + static int msic_power_up_pb(unsigned int device) { - struct sc_reg_access sc_access1[] = { + struct sc_reg_access vaud[] = { /* turn on the audio power supplies */ - {0x0DB, 0x05, 0}, + {0x0DB, 0x07, 0}, + }; + struct sc_reg_access pll[] = { + /* turn on PLL */ + {0x240, 0x20, 0}, + }; + struct sc_reg_access vhs[] = { /* VHSP */ - {0x0DC, 0xFF, 0}, + {0x0DC, 0x3D, 0}, /* VHSN */ {0x0DD, 0x3F, 0}, - /* turn on PLL */ - {0x240, 0x21, 0}, }; - struct sc_reg_access sc_access2[] = { + struct sc_reg_access hsdac[] = { + {0x382, 0x40, 0x40}, /* disable driver */ {0x25D, 0x0, 0x43}, /* DAC CONFIG ; both HP, LP on */ {0x257, 0x03, 0x03}, }; - struct sc_reg_access sc_access3[] = { + struct sc_reg_access hs_filter[] = { /* HSEPRXCTRL Enable the headset left and right FIR filters */ {0x250, 0x30, 0}, /* HSMIXER */ {0x256, 0x11, 0}, }; - struct sc_reg_access sc_access4[] = { + struct sc_reg_access hs_enable[] = { /* enable driver */ {0x25D, 0x3, 0x3}, + {0x26C, 0x0, 0x2}, /* unmute the headset */ { 0x259, 0x80, 0x80}, { 0x25A, 0x80, 0x80}, }; - struct sc_reg_access sc_access_vihf[] = { + struct sc_reg_access vihf[] = { /* VIHF ON */ - {0x0C9, 0x2D, 0x00}, + {0x0C9, 0x27, 0x00}, }; - struct sc_reg_access sc_access22[] = { + struct sc_reg_access ihf_filter[] = { /* disable driver */ {0x25D, 0x00, 0x0C}, /*Filer DAC enable*/ {0x251, 0x03, 0x03}, {0x257, 0x0C, 0x0C}, }; - struct sc_reg_access sc_access32[] = { + struct sc_reg_access ihf_en[] = { /*enable drv*/ {0x25D, 0x0C, 0x0c}, }; - struct sc_reg_access sc_access42[] = { + struct sc_reg_access ihf_unmute[] = { /*unmute headset*/ {0x25B, 0x80, 0x80}, {0x25C, 0x80, 0x80}, }; - struct sc_reg_access sc_access23[] = { + struct sc_reg_access epdac[] = { /* disable driver */ {0x25D, 0x0, 0x43}, /* DAC CONFIG ; both HP, LP on */ {0x257, 0x03, 0x03}, }; - struct sc_reg_access sc_access43[] = { + struct sc_reg_access ep_enable[] = { /* enable driver */ {0x25D, 0x40, 0x40}, /* unmute the headset */ { 0x259, 0x80, 0x80}, { 0x25A, 0x80, 0x80}, }; - struct sc_reg_access sc_access_vib[] = { + struct sc_reg_access vib1_en[] = { /* enable driver, ADC */ {0x25D, 0x10, 0x10}, - {0x264, 0x02, 0x02}, + {0x264, 0x02, 0x82}, }; - struct sc_reg_access sc_access_hap[] = { + struct sc_reg_access vib2_en[] = { /* enable driver, ADC */ {0x25D, 0x20, 0x20}, - {0x26A, 0x02, 0x02}, + {0x26A, 0x02, 0x82}, }; - struct sc_reg_access sc_access_pcm2[] = { + struct sc_reg_access pcm2_en[] = { /* enable pcm 2 */ {0x27C, 0x1, 0x1}, }; @@ -176,89 +425,95 @@ static int msic_power_up_pb(unsigned int device) } pr_debug("powering up pb.... Device %d\n", device); - sst_sc_reg_access(sc_access1, PMIC_WRITE, 4); + sst_sc_reg_access(vaud, PMIC_WRITE, 1); + msleep(1); + sst_sc_reg_access(pll, PMIC_WRITE, 1); + msleep(1); switch (device) { case SND_SST_DEVICE_HEADSET: + snd_msic_ops.pb_on = 1; + snd_msic_ops.pbhs_on = 1; if (snd_msic_ops.output_dev_id == STEREO_HEADPHONE) { - sst_sc_reg_access(sc_access2, PMIC_READ_MODIFY, 2); - sst_sc_reg_access(sc_access3, PMIC_WRITE, 2); - sst_sc_reg_access(sc_access4, PMIC_READ_MODIFY, 3); + sst_sc_reg_access(vhs, PMIC_WRITE, 2); + sst_sc_reg_access(hsdac, PMIC_READ_MODIFY, 3); + sst_sc_reg_access(hs_filter, PMIC_WRITE, 2); + sst_sc_reg_access(hs_enable, PMIC_READ_MODIFY, 4); } else { - sst_sc_reg_access(sc_access23, PMIC_READ_MODIFY, 2); - sst_sc_reg_access(sc_access3, PMIC_WRITE, 2); - sst_sc_reg_access(sc_access43, PMIC_READ_MODIFY, 3); + sst_sc_reg_access(epdac, PMIC_READ_MODIFY, 2); + sst_sc_reg_access(hs_filter, PMIC_WRITE, 2); + sst_sc_reg_access(ep_enable, PMIC_READ_MODIFY, 3); } - snd_msic_ops.pb_on = 1; + if (snd_msic_ops.lineout_dev_id == HEADSET) + msic_set_selected_lineout_dev(HEADSET); break; - case SND_SST_DEVICE_IHF: - sst_sc_reg_access(sc_access_vihf, PMIC_WRITE, 1); - sst_sc_reg_access(sc_access22, PMIC_READ_MODIFY, 3); - sst_sc_reg_access(sc_access32, PMIC_READ_MODIFY, 1); - sst_sc_reg_access(sc_access42, PMIC_READ_MODIFY, 2); + snd_msic_ops.pb_on = 1; + sst_sc_reg_access(vihf, PMIC_WRITE, 1); + sst_sc_reg_access(ihf_filter, PMIC_READ_MODIFY, 3); + sst_sc_reg_access(ihf_en, PMIC_READ_MODIFY, 1); + sst_sc_reg_access(ihf_unmute, PMIC_READ_MODIFY, 2); + if (snd_msic_ops.lineout_dev_id == IHF) + msic_set_selected_lineout_dev(IHF); break; case SND_SST_DEVICE_VIBRA: - sst_sc_reg_access(sc_access_vib, PMIC_READ_MODIFY, 2); + snd_msic_ops.pb_on = 1; + sst_sc_reg_access(vib1_en, PMIC_READ_MODIFY, 2); + if (snd_msic_ops.lineout_dev_id == VIBRA1) + msic_set_selected_lineout_dev(VIBRA1); break; case SND_SST_DEVICE_HAPTIC: - sst_sc_reg_access(sc_access_hap, PMIC_READ_MODIFY, 2); + snd_msic_ops.pb_on = 1; + sst_sc_reg_access(vib2_en, PMIC_READ_MODIFY, 2); + if (snd_msic_ops.lineout_dev_id == VIBRA2) + msic_set_selected_lineout_dev(VIBRA2); break; default: pr_warn("Wrong Device %d, selected %d\n", device, snd_msic_ops.output_dev_id); } - return sst_sc_reg_access(sc_access_pcm2, PMIC_READ_MODIFY, 1); + return sst_sc_reg_access(pcm2_en, PMIC_READ_MODIFY, 1); } static int msic_power_up_cp(unsigned int device) { - struct sc_reg_access sc_access[] = { + struct sc_reg_access vaud[] = { /* turn on the audio power supplies */ - {0x0DB, 0x05, 0}, - /* VHSP */ - {0x0DC, 0xFF, 0}, - /* VHSN */ - {0x0DD, 0x3F, 0}, + {0x0DB, 0x07, 0}, + }; + struct sc_reg_access pll[] = { /* turn on PLL */ - {0x240, 0x21, 0}, - - /* Turn on DMIC supply */ - {0x247, 0xA0, 0x0}, - {0x240, 0x21, 0x0}, - {0x24C, 0x10, 0x0}, - + {0x240, 0x20, 0}, + }; + struct sc_reg_access dmic_bias[] = { + /* Turn on AMIC supply */ + {0x247, 0xA0, 0xA0}, + }; + struct sc_reg_access dmic[] = { /* mic demux enable */ - {0x245, 0x3F, 0x0}, - {0x246, 0x7, 0x0}, + {0x245, 0x3F, 0x3F}, + {0x246, 0x07, 0x07}, }; - struct sc_reg_access sc_access_amic[] = { - /* turn on the audio power supplies */ - {0x0DB, 0x05, 0}, - /* VHSP */ - {0x0DC, 0xFF, 0}, - /* VHSN */ - {0x0DD, 0x3F, 0}, - /* turn on PLL */ - {0x240, 0x21, 0}, - /*ADC EN*/ - {0x248, 0x05, 0x0}, - {0x24C, 0x76, 0x0}, - /*MIC EN*/ - {0x249, 0x09, 0x0}, - {0x24A, 0x09, 0x0}, + struct sc_reg_access amic_bias[] = { /* Turn on AMIC supply */ - {0x247, 0xFC, 0x0}, + {0x247, 0xFC, 0xFC}, + }; + struct sc_reg_access amic[] = { + /*MIC EN*/ + {0x249, 0x01, 0x01}, + {0x24A, 0x01, 0x01}, + /*ADC EN*/ + {0x248, 0x05, 0x0F}, }; - struct sc_reg_access sc_access2[] = { + struct sc_reg_access pcm2[] = { /* enable pcm 2 */ {0x27C, 0x1, 0x1}, }; - struct sc_reg_access sc_access3[] = { + struct sc_reg_access tx_on[] = { /*wait for mic to stabalize before turning on audio channels*/ {0x24F, 0x3C, 0x0}, }; @@ -271,42 +526,161 @@ static int msic_power_up_cp(unsigned int device) } pr_debug("powering up cp....%d\n", snd_msic_ops.input_dev_id); - sst_sc_reg_access(sc_access2, PMIC_READ_MODIFY, 1); + sst_sc_reg_access(vaud, PMIC_WRITE, 1); + msleep(500);/*FIXME need optimzed value here*/ + sst_sc_reg_access(pll, PMIC_WRITE, 1); + msleep(1); snd_msic_ops.cap_on = 1; - if (snd_msic_ops.input_dev_id == AMIC) - sst_sc_reg_access(sc_access_amic, PMIC_WRITE, 9); - else - sst_sc_reg_access(sc_access, PMIC_WRITE, 9); - return sst_sc_reg_access(sc_access3, PMIC_WRITE, 1); - + if (snd_msic_ops.input_dev_id == AMIC) { + sst_sc_reg_access(amic_bias, PMIC_READ_MODIFY, 1); + msleep(1); + sst_sc_reg_access(amic, PMIC_READ_MODIFY, 3); + } else { + sst_sc_reg_access(dmic_bias, PMIC_READ_MODIFY, 1); + msleep(1); + sst_sc_reg_access(dmic, PMIC_READ_MODIFY, 2); + } + msleep(1); + sst_sc_reg_access(tx_on, PMIC_WRITE, 1); + return sst_sc_reg_access(pcm2, PMIC_READ_MODIFY, 1); } static int msic_power_down(void) { - int retval = 0; + struct sc_reg_access power_dn[] = { + /* VHSP */ + {0x0DC, 0xC4, 0}, + /* VHSN */ + {0x0DD, 0x04, 0}, + /* VIHF */ + {0x0C9, 0x24, 0}, + }; + struct sc_reg_access pll[] = { + /* turn off PLL*/ + {0x240, 0x00, 0x0}, + }; + struct sc_reg_access vaud[] = { + /* turn off VAUD*/ + {0x0DB, 0x04, 0}, + }; pr_debug("powering dn msic\n"); + snd_msic_ops.pbhs_on = 0; snd_msic_ops.pb_on = 0; snd_msic_ops.cap_on = 0; - return retval; + sst_sc_reg_access(power_dn, PMIC_WRITE, 3); + msleep(1); + sst_sc_reg_access(pll, PMIC_WRITE, 1); + msleep(1); + sst_sc_reg_access(vaud, PMIC_WRITE, 1); + return 0; } -static int msic_power_down_pb(void) +static int msic_power_down_pb(unsigned int device) { - int retval = 0; + struct sc_reg_access drv_enable[] = { + {0x25D, 0x00, 0x00}, + }; + struct sc_reg_access hs_mute[] = { + {0x259, 0x80, 0x80}, + {0x25A, 0x80, 0x80}, + {0x26C, 0x02, 0x02}, + }; + struct sc_reg_access hs_off[] = { + {0x257, 0x00, 0x03}, + {0x250, 0x00, 0x30}, + {0x382, 0x00, 0x40}, + }; + struct sc_reg_access ihf_mute[] = { + {0x25B, 0x80, 0x80}, + {0x25C, 0x80, 0x80}, + }; + struct sc_reg_access ihf_off[] = { + {0x257, 0x00, 0x0C}, + {0x251, 0x00, 0x03}, + }; + struct sc_reg_access vib1_off[] = { + {0x264, 0x00, 0x82}, + }; + struct sc_reg_access vib2_off[] = { + {0x26A, 0x00, 0x82}, + }; + struct sc_reg_access lout_off[] = { + {0x25e, 0x66, 0x00}, + }; + struct sc_reg_access pmode_disable[] = { + {0x381, 0x00, 0x10}, + }; - pr_debug("powering dn pb....\n"); - snd_msic_ops.pb_on = 0; - return retval; + + + pr_debug("powering dn pb for device %d\n", device); + switch (device) { + case SND_SST_DEVICE_HEADSET: + snd_msic_ops.pbhs_on = 0; + sst_sc_reg_access(hs_mute, PMIC_READ_MODIFY, 3); + drv_enable[0].mask = 0x43; + sst_sc_reg_access(drv_enable, PMIC_READ_MODIFY, 1); + sst_sc_reg_access(hs_off, PMIC_READ_MODIFY, 3); + if (snd_msic_ops.lineout_dev_id == HEADSET) + sst_sc_reg_access(lout_off, PMIC_WRITE, 1); + break; + + case SND_SST_DEVICE_IHF: + sst_sc_reg_access(ihf_mute, PMIC_READ_MODIFY, 2); + drv_enable[0].mask = 0x0C; + sst_sc_reg_access(drv_enable, PMIC_READ_MODIFY, 1); + sst_sc_reg_access(ihf_off, PMIC_READ_MODIFY, 2); + if (snd_msic_ops.lineout_dev_id == IHF) { + sst_sc_reg_access(lout_off, PMIC_WRITE, 1); + sst_sc_reg_access(pmode_disable, PMIC_READ_MODIFY, 1); + } + break; + + case SND_SST_DEVICE_VIBRA: + sst_sc_reg_access(vib1_off, PMIC_READ_MODIFY, 1); + drv_enable[0].mask = 0x10; + sst_sc_reg_access(drv_enable, PMIC_READ_MODIFY, 1); + if (snd_msic_ops.lineout_dev_id == VIBRA1) + sst_sc_reg_access(lout_off, PMIC_WRITE, 1); + break; + + case SND_SST_DEVICE_HAPTIC: + sst_sc_reg_access(vib2_off, PMIC_READ_MODIFY, 1); + drv_enable[0].mask = 0x20; + sst_sc_reg_access(drv_enable, PMIC_READ_MODIFY, 1); + if (snd_msic_ops.lineout_dev_id == VIBRA2) + sst_sc_reg_access(lout_off, PMIC_WRITE, 1); + break; + } + return 0; } -static int msic_power_down_cp(void) +static int msic_power_down_cp(unsigned int device) { - int retval = 0; + struct sc_reg_access dmic[] = { + {0x247, 0x00, 0xA0}, + {0x245, 0x00, 0x38}, + {0x246, 0x00, 0x07}, + }; + struct sc_reg_access amic[] = { + {0x248, 0x00, 0x05}, + {0x249, 0x00, 0x01}, + {0x24A, 0x00, 0x01}, + {0x247, 0x00, 0xA3}, + }; + struct sc_reg_access tx_off[] = { + {0x24F, 0x00, 0x3C}, + }; pr_debug("powering dn cp....\n"); snd_msic_ops.cap_on = 0; - return retval; + sst_sc_reg_access(tx_off, PMIC_READ_MODIFY, 1); + if (snd_msic_ops.input_dev_id == DMIC) + sst_sc_reg_access(dmic, PMIC_READ_MODIFY, 3); + else + sst_sc_reg_access(amic, PMIC_READ_MODIFY, 4); + return 0; } static int msic_set_selected_output_dev(u8 value) @@ -315,7 +689,7 @@ static int msic_set_selected_output_dev(u8 value) pr_debug("msic set selected output:%d\n", value); snd_msic_ops.output_dev_id = value; - if (snd_msic_ops.pb_on) + if (snd_msic_ops.pbhs_on) msic_power_up_pb(SND_SST_DEVICE_HEADSET); return retval; } @@ -352,6 +726,57 @@ static int msic_set_selected_input_dev(u8 value) return retval; } +static int msic_set_hw_dmic_route(u8 hw_ch_index) +{ + struct sc_reg_access sc_access_router; + int retval = -EINVAL; + + switch (hw_ch_index) { + case HW_CH0: + sc_access_router.reg_addr = AUDIOMUX12; + sc_access_router.value = snd_msic_ops.hw_dmic_map[0]; + sc_access_router.mask = (MASK2 | MASK1 | MASK0); + pr_debug("hw_ch0. value = 0x%x\n", + sc_access_router.value); + retval = sst_sc_reg_access(&sc_access_router, + PMIC_READ_MODIFY, 1); + break; + + case HW_CH1: + sc_access_router.reg_addr = AUDIOMUX12; + sc_access_router.value = (snd_msic_ops.hw_dmic_map[1]) << 4; + sc_access_router.mask = (MASK6 | MASK5 | MASK4); + pr_debug("### hw_ch1. value = 0x%x\n", + sc_access_router.value); + retval = sst_sc_reg_access(&sc_access_router, + PMIC_READ_MODIFY, 1); + break; + + case HW_CH2: + sc_access_router.reg_addr = AUDIOMUX34; + sc_access_router.value = snd_msic_ops.hw_dmic_map[2]; + sc_access_router.mask = (MASK2 | MASK1 | MASK0); + pr_debug("hw_ch2. value = 0x%x\n", + sc_access_router.value); + retval = sst_sc_reg_access(&sc_access_router, + PMIC_READ_MODIFY, 1); + break; + + case HW_CH3: + sc_access_router.reg_addr = AUDIOMUX34; + sc_access_router.value = (snd_msic_ops.hw_dmic_map[3]) << 4; + sc_access_router.mask = (MASK6 | MASK5 | MASK4); + pr_debug("hw_ch3. value = 0x%x\n", + sc_access_router.value); + retval = sst_sc_reg_access(&sc_access_router, + PMIC_READ_MODIFY, 1); + break; + } + + return retval; +} + + static int msic_set_pcm_voice_params(void) { return 0; @@ -392,9 +817,215 @@ static int msic_get_vol(int dev_id, int *value) return 0; } +static int msic_set_headset_state(int state) +{ + struct sc_reg_access hs_enable[] = { + {0x25D, 0x03, 0x03}, + }; + + if (state) + /*enable*/ + sst_sc_reg_access(hs_enable, PMIC_READ_MODIFY, 1); + else { + hs_enable[0].value = 0; + sst_sc_reg_access(hs_enable, PMIC_READ_MODIFY, 1); + } + return 0; +} + +static int msic_enable_mic_bias(void) +{ + struct sc_reg_access jack_interrupt_reg[] = { + {0x0DB, 0x07, 0x00}, + + }; + struct sc_reg_access jack_bias_reg[] = { + {0x247, 0x0C, 0x0C}, + }; + + sst_sc_reg_access(jack_interrupt_reg, PMIC_WRITE, 1); + sst_sc_reg_access(jack_bias_reg, PMIC_READ_MODIFY, 1); + return 0; +} + +static int msic_disable_mic_bias(void) +{ + if (snd_msic_ops.jack_interrupt_status == true) + return 0; + if (!(snd_msic_ops.pb_on || snd_msic_ops.cap_on)) + msic_power_down(); + return 0; +} + +static int msic_disable_jack_btn(void) +{ + struct sc_reg_access btn_disable[] = { + {0x26C, 0x00, 0x01} + }; + + if (!(snd_msic_ops.pb_on || snd_msic_ops.cap_on)) + msic_power_down(); + snd_msic_ops.jack_interrupt_status = false; + return sst_sc_reg_access(btn_disable, PMIC_READ_MODIFY, 1); +} + +static int msic_enable_jack_btn(void) +{ + struct sc_reg_access btn_enable[] = { + {0x26b, 0x77, 0x00}, + {0x26C, 0x01, 0x00}, + }; + return sst_sc_reg_access(btn_enable, PMIC_WRITE, 2); +} +static int msic_convert_adc_to_mvolt(unsigned int mic_bias) +{ + return (ADC_ONE_LSB_MULTIPLIER * mic_bias) / 1000; +} +int msic_get_headset_state(int mic_bias) +{ + struct sc_reg_access msic_hs_toggle[] = { + {0x070, 0x00, 0x01}, + }; + if (mic_bias >= 0 && mic_bias < 400) { + + pr_debug("Detected Headphone!!!\n"); + sst_sc_reg_access(msic_hs_toggle, PMIC_READ_MODIFY, 1); + + } else if (mic_bias > 400 && mic_bias < 650) { + + pr_debug("Detected American headset\n"); + msic_hs_toggle[0].value = 0x01; + sst_sc_reg_access(msic_hs_toggle, PMIC_READ_MODIFY, 1); + + } else if (mic_bias >= 650 && mic_bias < 2000) { + + pr_debug("Detected Headset!!!\n"); + sst_sc_reg_access(msic_hs_toggle, PMIC_READ_MODIFY, 1); + /*power on jack and btn*/ + snd_msic_ops.jack_interrupt_status = true; + msic_enable_jack_btn(); + msic_enable_mic_bias(); + return SND_JACK_HEADSET; + + } else + pr_debug("Detected Open Cable!!!\n"); + + return SND_JACK_HEADPHONE; +} + +static int msic_get_mic_bias(void *arg) +{ + struct snd_intelmad *intelmad_drv = (struct snd_intelmad *)arg; + u16 adc_adr = intelmad_drv->adc_address; + u16 adc_val; + int ret; + struct sc_reg_access adc_ctrl3[2] = { + {0x1C2, 0x05, 0x0}, + }; + + struct sc_reg_access audio_adc_reg1 = {0,}; + struct sc_reg_access audio_adc_reg2 = {0,}; + + msic_enable_mic_bias(); + /* Enable the msic for conversion before reading */ + ret = sst_sc_reg_access(adc_ctrl3, PMIC_WRITE, 1); + if (ret) + return ret; + adc_ctrl3[0].value = 0x04; + /* Re-toggle the RRDATARD bit */ + ret = sst_sc_reg_access(adc_ctrl3, PMIC_WRITE, 1); + if (ret) + return ret; + + audio_adc_reg1.reg_addr = adc_adr; + /* Read the higher bits of data */ + msleep(1000); + ret = sst_sc_reg_access(&audio_adc_reg1, PMIC_READ, 1); + if (ret) + return ret; + pr_debug("adc read value %x", audio_adc_reg1.value); + + /* Shift bits to accomodate the lower two data bits */ + adc_val = (audio_adc_reg1.value << 2); + adc_adr++; + audio_adc_reg2. reg_addr = adc_adr; + ret = sst_sc_reg_access(&audio_adc_reg2, PMIC_READ, 1); + if (ret) + return ret; + pr_debug("adc read value %x", audio_adc_reg2.value); + + /* Adding lower two bits to the higher bits */ + audio_adc_reg2.value &= 03; + adc_val += audio_adc_reg2.value; + + pr_debug("ADC value 0x%x", adc_val); + msic_disable_mic_bias(); + return adc_val; +} + +static void msic_pmic_irq_cb(void *cb_data, u8 intsts) +{ + struct mad_jack *mjack = NULL; + unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; + struct snd_intelmad *intelmaddata = cb_data; + int retval = 0; + + pr_debug("value returned = 0x%x\n", intsts); + + if (snd_msic_ops.card_status == SND_CARD_UN_INIT) { + retval = msic_init_card(); + if (retval) + return; + } + + mjack = &intelmaddata->jack[0]; + if (intsts & 0x1) { + pr_debug("MAD short_push detected\n"); + present = SND_JACK_BTN_0; + jack_event_flag = buttonpressflag = 1; + mjack->jack.type = SND_JACK_BTN_0; + mjack->jack.key[0] = BTN_0 ; + } + + if (intsts & 0x2) { + pr_debug(":MAD long_push detected\n"); + jack_event_flag = buttonpressflag = 1; + mjack->jack.type = present = SND_JACK_BTN_1; + mjack->jack.key[1] = BTN_1; + } + + if (intsts & 0x4) { + unsigned int mic_bias; + jack_event_flag = 1; + buttonpressflag = 0; + mic_bias = msic_get_mic_bias(intelmaddata); + pr_debug("mic_bias = %d\n", mic_bias); + mic_bias = msic_convert_adc_to_mvolt(mic_bias); + pr_debug("mic_bias after conversion = %d mV\n", mic_bias); + mjack->jack_dev_state = msic_get_headset_state(mic_bias); + mjack->jack.type = present = mjack->jack_dev_state; + } + + if (intsts & 0x8) { + mjack->jack.type = mjack->jack_dev_state; + present = 0; + jack_event_flag = 1; + buttonpressflag = 0; + msic_disable_jack_btn(); + msic_disable_mic_bias(); + } + if (jack_event_flag) + sst_mad_send_jack_report(&mjack->jack, + buttonpressflag, present); +} + + + struct snd_pmic_ops snd_msic_ops = { .set_input_dev = msic_set_selected_input_dev, .set_output_dev = msic_set_selected_output_dev, + .set_lineout_dev = msic_set_selected_lineout_dev, + .set_hw_dmic_route = msic_set_hw_dmic_route, .set_mute = msic_set_mute, .get_mute = msic_get_mute, .set_vol = msic_set_vol, @@ -408,5 +1039,9 @@ struct snd_pmic_ops snd_msic_ops = { .power_up_pmic_cp = msic_power_up_cp, .power_down_pmic_pb = msic_power_down_pb, .power_down_pmic_cp = msic_power_down_cp, - .power_down_pmic = msic_power_down, + .power_down_pmic = msic_power_down, + .pmic_irq_cb = msic_pmic_irq_cb, + .pmic_jack_enable = msic_enable_mic_bias, + .pmic_get_mic_bias = msic_get_mic_bias, + .pmic_set_headset_state = msic_set_headset_state, }; diff --git a/drivers/staging/intel_sst/intelmid_pvt.c b/drivers/staging/intel_sst/intelmid_pvt.c index 3ba9daf67526..90e0e64c0ab0 100644 --- a/drivers/staging/intel_sst/intelmid_pvt.c +++ b/drivers/staging/intel_sst/intelmid_pvt.c @@ -31,7 +31,6 @@ #include <sound/core.h> #include <sound/control.h> #include <sound/pcm.h> -#include "jack.h" #include "intel_sst.h" #include "intel_sst_ioctl.h" #include "intelmid_snd_control.h" diff --git a/drivers/staging/intel_sst/intelmid_snd_control.h b/drivers/staging/intel_sst/intelmid_snd_control.h index a4565f33a91b..06ad3a10099c 100644 --- a/drivers/staging/intel_sst/intelmid_snd_control.h +++ b/drivers/staging/intel_sst/intelmid_snd_control.h @@ -80,6 +80,13 @@ enum SND_INPUT_DEVICE { HS_MIC, IN_UNDEFINED }; +enum SND_LINE_OUT_DEVICE { + HEADSET, + IHF, + VIBRA1, + VIBRA2, + NONE, +}; enum SND_OUTPUT_DEVICE { STEREO_HEADPHONE, @@ -104,6 +111,8 @@ enum pmic_controls { PMIC_SND_RIGHT_SPEAKER_MUTE = 0x0015, PMIC_SND_RECEIVER_VOL = 0x0016, PMIC_SND_RECEIVER_MUTE = 0x0017, + PMIC_SND_LEFT_MASTER_VOL = 0x0018, + PMIC_SND_RIGHT_MASTER_VOL = 0x0019, /* Other controls */ PMIC_SND_MUTE_ALL = 0x0020, PMIC_MAX_CONTROLS = 0x0020, diff --git a/drivers/staging/intel_sst/intelmid_v0_control.c b/drivers/staging/intel_sst/intelmid_v0_control.c index 7756f8feaf85..b8dfdb9bc1aa 100644 --- a/drivers/staging/intel_sst/intelmid_v0_control.c +++ b/drivers/staging/intel_sst/intelmid_v0_control.c @@ -30,9 +30,10 @@ #include <linux/pci.h> #include <linux/file.h> +#include <sound/control.h> #include "intel_sst.h" #include "intelmid_snd_control.h" - +#include "intelmid.h" enum _reg_v1 { VOICEPORT1 = 0x180, @@ -64,6 +65,7 @@ enum _reg_v1 { }; int rev_id = 0x20; +static bool jack_det_enabled; /**** * fs_init_card - initialize the sound card @@ -157,7 +159,7 @@ static int fs_power_up_pb(unsigned int port) return fs_enable_audiodac(UNMUTE); } -static int fs_power_down_pb(void) +static int fs_power_down_pb(unsigned int device) { struct sc_reg_access sc_access[] = { {POWERCTRL1, 0x00, 0xC6}, @@ -195,7 +197,7 @@ static int fs_power_up_cp(unsigned int port) return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); } -static int fs_power_down_cp(void) +static int fs_power_down_cp(unsigned int device) { struct sc_reg_access sc_access[] = { {POWERCTRL2, 0x00, 0x03}, @@ -753,6 +755,90 @@ static int fs_get_vol(int dev_id, int *value) return retval; } +static void fs_pmic_irq_enable(void *data) +{ + struct snd_intelmad *intelmaddata = data; + struct sc_reg_access sc_access[] = { + {0x187, 0x00, MASK7}, + {0x188, 0x10, MASK4}, + {0x18b, 0x10, MASK4}, + }; + + struct sc_reg_access sc_access_write[] = { + {0x198, 0x00, 0x0}, + }; + pr_debug("Audio interrupt enable\n"); + sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 3); + sst_sc_reg_access(sc_access_write, PMIC_WRITE, 1); + + intelmaddata->jack[0].jack_status = 0; + /*intelmaddata->jack[1].jack_status = 0;*/ + + jack_det_enabled = true; + return; +} + +static void fs_pmic_irq_cb(void *cb_data, u8 value) +{ + struct mad_jack *mjack = NULL; + struct snd_intelmad *intelmaddata = cb_data; + unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; + + mjack = &intelmaddata->jack[0]; + + if (value & 0x4) { + if (!jack_det_enabled) + fs_pmic_irq_enable(intelmaddata); + + /* send headphone detect */ + pr_debug(":MAD headphone %d\n", value & 0x4); + present = !(mjack->jack_status); + mjack->jack_status = present; + jack_event_flag = 1; + mjack->jack.type = SND_JACK_HEADPHONE; + } + + if (value & 0x2) { + /* send short push */ + pr_debug(":MAD short push %d\n", value & 0x2); + present = 1; + jack_event_flag = 1; + buttonpressflag = 1; + mjack->jack.type = MID_JACK_HS_SHORT_PRESS; + } + + if (value & 0x1) { + /* send long push */ + pr_debug(":MAD long push %d\n", value & 0x1); + present = 1; + jack_event_flag = 1; + buttonpressflag = 1; + mjack->jack.type = MID_JACK_HS_LONG_PRESS; + } + + if (value & 0x8) { + if (!jack_det_enabled) + fs_pmic_irq_enable(intelmaddata); + /* send headset detect */ + pr_debug(":MAD headset = %d\n", value & 0x8); + present = !(mjack->jack_status); + mjack->jack_status = present; + jack_event_flag = 1; + mjack->jack.type = SND_JACK_HEADSET; + } + + + if (jack_event_flag) + sst_mad_send_jack_report(&mjack->jack, + buttonpressflag, present); + + return; +} +static int fs_jack_enable(void) +{ + return 0; +} + struct snd_pmic_ops snd_pmic_ops_fs = { .set_input_dev = fs_set_selected_input_dev, .set_output_dev = fs_set_selected_output_dev, @@ -765,9 +851,16 @@ struct snd_pmic_ops snd_pmic_ops_fs = { .set_pcm_voice_params = fs_set_pcm_voice_params, .set_voice_port = fs_set_voice_port, .set_audio_port = fs_set_audio_port, - .power_up_pmic_pb = fs_power_up_pb, - .power_up_pmic_cp = fs_power_up_cp, - .power_down_pmic_pb = fs_power_down_pb, - .power_down_pmic_cp = fs_power_down_cp, - .power_down_pmic = fs_power_down, + .power_up_pmic_pb = fs_power_up_pb, + .power_up_pmic_cp = fs_power_up_cp, + .power_down_pmic_pb = fs_power_down_pb, + .power_down_pmic_cp = fs_power_down_cp, + .power_down_pmic = fs_power_down, + .pmic_irq_cb = fs_pmic_irq_cb, + /* + * Jack detection enabling + * need be delayed till first IRQ happen. + */ + .pmic_irq_enable = NULL, + .pmic_jack_enable = fs_jack_enable, }; diff --git a/drivers/staging/intel_sst/intelmid_v1_control.c b/drivers/staging/intel_sst/intelmid_v1_control.c index 1ea814218059..9d00728d8deb 100644 --- a/drivers/staging/intel_sst/intelmid_v1_control.c +++ b/drivers/staging/intel_sst/intelmid_v1_control.c @@ -32,7 +32,6 @@ #include <linux/file.h> #include <asm/mrst.h> #include <sound/pcm.h> -#include "jack.h" #include <sound/pcm_params.h> #include <sound/control.h> #include <sound/initval.h> @@ -212,7 +211,7 @@ static int mx_power_up_pb(unsigned int port) return mx_enable_audiodac(UNMUTE); } -static int mx_power_down_pb(void) +static int mx_power_down_pb(unsigned int device) { struct sc_reg_access sc_access[3]; int retval = 0; @@ -255,7 +254,7 @@ static int mx_power_up_cp(unsigned int port) return sst_sc_reg_access(sc_access, PMIC_READ_MODIFY, 2); } -static int mx_power_down_cp(void) +static int mx_power_down_cp(unsigned int device) { struct sc_reg_access sc_access[] = { {ENABLE_OPDEV_CTRL, 0x00, MASK1|MASK0}, @@ -832,6 +831,129 @@ static int mx_get_vol(int dev_id, int *value) return retval; } +static u8 mx_get_jack_status(void) +{ + struct sc_reg_access sc_access_read = {0,}; + + sc_access_read.reg_addr = 0x201; + sst_sc_reg_access(&sc_access_read, PMIC_READ, 1); + pr_debug("value returned = 0x%x\n", sc_access_read.value); + return sc_access_read.value; +} + +static void mx_pmic_irq_enable(void *data) +{ + struct snd_intelmad *intelmaddata = data; + + intelmaddata->jack_prev_state = 0xc0; + return; +} + +static void mx_pmic_irq_cb(void *cb_data, u8 intsts) +{ + u8 jack_cur_status, jack_prev_state = 0; + struct mad_jack *mjack = NULL; + unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; + time_t timediff; + struct snd_intelmad *intelmaddata = cb_data; + + mjack = &intelmaddata->jack[0]; + if (intsts & 0x2) { + jack_cur_status = mx_get_jack_status(); + jack_prev_state = intelmaddata->jack_prev_state; + if ((jack_prev_state == 0xc0) && (jack_cur_status == 0x40)) { + /*headset insert detected. */ + pr_debug("MAD headset inserted\n"); + present = 1; + jack_event_flag = 1; + mjack->jack_status = 1; + mjack->jack.type = SND_JACK_HEADSET; + } + + if ((jack_prev_state == 0xc0) && (jack_cur_status == 0x00)) { + /* headphone insert detected. */ + pr_debug("MAD headphone inserted\n"); + present = 1; + jack_event_flag = 1; + mjack->jack.type = SND_JACK_HEADPHONE; + } + + if ((jack_prev_state == 0x40) && (jack_cur_status == 0xc0)) { + /* headset remove detected. */ + pr_debug("MAD headset removed\n"); + + present = 0; + jack_event_flag = 1; + mjack->jack_status = 0; + mjack->jack.type = SND_JACK_HEADSET; + } + + if ((jack_prev_state == 0x00) && (jack_cur_status == 0xc0)) { + /* headphone remove detected. */ + pr_debug("MAD headphone removed\n"); + present = 0; + jack_event_flag = 1; + mjack->jack.type = SND_JACK_HEADPHONE; + } + + if ((jack_prev_state == 0x40) && (jack_cur_status == 0x00)) { + /* button pressed */ + do_gettimeofday(&mjack->buttonpressed); + pr_debug("MAD button press detected\n"); + } + + if ((jack_prev_state == 0x00) && (jack_cur_status == 0x40)) { + if (mjack->jack_status) { + /*button pressed */ + do_gettimeofday( + &mjack->buttonreleased); + /*button pressed */ + pr_debug("MAD Button Released detected\n"); + timediff = mjack->buttonreleased.tv_sec - + mjack->buttonpressed.tv_sec; + buttonpressflag = 1; + + if (timediff > 1) { + pr_debug("MAD long press dtd\n"); + /* send headphone detect/undetect */ + present = 1; + jack_event_flag = 1; + mjack->jack.type = + MID_JACK_HS_LONG_PRESS; + } else { + pr_debug("MAD short press dtd\n"); + /* send headphone detect/undetect */ + present = 1; + jack_event_flag = 1; + mjack->jack.type = + MID_JACK_HS_SHORT_PRESS; + } + } else { + /***workaround for maxim + hw issue,0x00 t 0x40 is not + a valid transiton for Headset insertion */ + /*headset insert detected. */ + pr_debug("MAD headset inserted\n"); + present = 1; + jack_event_flag = 1; + mjack->jack_status = 1; + mjack->jack.type = SND_JACK_HEADSET; + } + } + intelmaddata->jack_prev_state = jack_cur_status; + pr_debug("mx_pmic_irq_cb prv_state= 0x%x\n", + intelmaddata->jack_prev_state); + } + + if (jack_event_flag) + sst_mad_send_jack_report(&mjack->jack, + buttonpressflag, present); +} +static int mx_jack_enable(void) +{ + return 0; +} + struct snd_pmic_ops snd_pmic_ops_mx = { .set_input_dev = mx_set_selected_input_dev, .set_output_dev = mx_set_selected_output_dev, @@ -844,10 +966,13 @@ struct snd_pmic_ops snd_pmic_ops_mx = { .set_pcm_voice_params = mx_set_pcm_voice_params, .set_voice_port = mx_set_voice_port, .set_audio_port = mx_set_audio_port, - .power_up_pmic_pb = mx_power_up_pb, - .power_up_pmic_cp = mx_power_up_cp, - .power_down_pmic_pb = mx_power_down_pb, - .power_down_pmic_cp = mx_power_down_cp, - .power_down_pmic = mx_power_down, + .power_up_pmic_pb = mx_power_up_pb, + .power_up_pmic_cp = mx_power_up_cp, + .power_down_pmic_pb = mx_power_down_pb, + .power_down_pmic_cp = mx_power_down_cp, + .power_down_pmic = mx_power_down, + .pmic_irq_cb = mx_pmic_irq_cb, + .pmic_irq_enable = mx_pmic_irq_enable, + .pmic_jack_enable = mx_jack_enable, }; diff --git a/drivers/staging/intel_sst/intelmid_v2_control.c b/drivers/staging/intel_sst/intelmid_v2_control.c index 3c6b3abff3c3..000378a35c1f 100644 --- a/drivers/staging/intel_sst/intelmid_v2_control.c +++ b/drivers/staging/intel_sst/intelmid_v2_control.c @@ -28,11 +28,14 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/gpio.h> #include <linux/pci.h> #include <linux/delay.h> #include <linux/file.h> +#include <sound/control.h> #include "intel_sst.h" #include "intelmid_snd_control.h" +#include "intelmid.h" enum reg_v3 { VAUDIOCNT = 0x51, @@ -82,8 +85,15 @@ enum reg_v3 { HPLMIXSEL = 0x12b, HPRMIXSEL = 0x12c, LOANTIPOP = 0x12d, + AUXDBNC = 0x12f, }; +static void nc_set_amp_power(int power) +{ + if (snd_pmic_ops_nc.gpio_amp) + gpio_set_value(snd_pmic_ops_nc.gpio_amp, power); +} + /**** * nc_init_card - initialize the sound card * @@ -111,18 +121,20 @@ static int nc_init_card(void) {VOICEVOL, 0x0e, 0}, {HPLVOL, 0x06, 0}, {HPRVOL, 0x06, 0}, - {MICCTRL, 0x41, 0x00}, + {MICCTRL, 0x51, 0x00}, {ADCSAMPLERATE, 0x8B, 0x00}, {MICSELVOL, 0x5B, 0x00}, {LILSEL, 0x06, 0}, {LIRSEL, 0x46, 0}, {LOANTIPOP, 0x00, 0}, {DMICCTRL1, 0x40, 0}, + {AUXDBNC, 0xff, 0}, }; snd_pmic_ops_nc.card_status = SND_CARD_INIT_DONE; snd_pmic_ops_nc.master_mute = UNMUTE; snd_pmic_ops_nc.mute_status = UNMUTE; - sst_sc_reg_access(sc_access, PMIC_WRITE, 26); + sst_sc_reg_access(sc_access, PMIC_WRITE, 27); + mutex_init(&snd_pmic_ops_nc.lock); pr_debug("init complete!!\n"); return 0; } @@ -169,6 +181,7 @@ static int nc_power_up_pb(unsigned int port) return retval; if (port == 0xFF) return 0; + mutex_lock(&snd_pmic_ops_nc.lock); nc_enable_audiodac(MUTE); msleep(30); @@ -209,8 +222,21 @@ static int nc_power_up_pb(unsigned int port) msleep(30); - return nc_enable_audiodac(UNMUTE); - + snd_pmic_ops_nc.pb_on = 1; + + /* + * There is a mismatch between Playback Sources and the enumerated + * values of output sources. This mismatch causes ALSA upper to send + * Item 1 for Internal Speaker, but the expected enumeration is 2! For + * now, treat MONO_EARPIECE and INTERNAL_SPKR identically and power up + * the needed resources + */ + if (snd_pmic_ops_nc.output_dev_id == MONO_EARPIECE || + snd_pmic_ops_nc.output_dev_id == INTERNAL_SPKR) + nc_set_amp_power(1); + nc_enable_audiodac(UNMUTE); + mutex_unlock(&snd_pmic_ops_nc.lock); + return 0; } static int nc_power_up_cp(unsigned int port) @@ -270,7 +296,6 @@ static int nc_power_down(void) int retval = 0; struct sc_reg_access sc_access[5]; - if (snd_pmic_ops_nc.card_status == SND_CARD_UN_INIT) retval = nc_init_card(); if (retval) @@ -280,6 +305,10 @@ static int nc_power_down(void) pr_debug("powering dn nc_power_down ....\n"); + if (snd_pmic_ops_nc.output_dev_id == MONO_EARPIECE || + snd_pmic_ops_nc.output_dev_id == INTERNAL_SPKR) + nc_set_amp_power(0); + msleep(30); sc_access[0].reg_addr = DRVPOWERCTRL; @@ -316,7 +345,7 @@ static int nc_power_down(void) return nc_enable_audiodac(UNMUTE); } -static int nc_power_down_pb(void) +static int nc_power_down_pb(unsigned int device) { int retval = 0; @@ -328,7 +357,7 @@ static int nc_power_down_pb(void) return retval; pr_debug("powering dn pb....\n"); - + mutex_lock(&snd_pmic_ops_nc.lock); nc_enable_audiodac(MUTE); @@ -355,12 +384,14 @@ static int nc_power_down_pb(void) msleep(30); - return nc_enable_audiodac(UNMUTE); - + snd_pmic_ops_nc.pb_on = 0; + nc_enable_audiodac(UNMUTE); + mutex_unlock(&snd_pmic_ops_nc.lock); + return 0; } -static int nc_power_down_cp(void) +static int nc_power_down_cp(unsigned int device) { struct sc_reg_access sc_access[] = { {POWERCTRL1, 0x00, 0xBE}, @@ -498,11 +529,13 @@ static int nc_set_selected_output_dev(u8 value) { struct sc_reg_access sc_access_HP[] = { {LMUTE, 0x02, 0x06}, - {RMUTE, 0x02, 0x06} + {RMUTE, 0x02, 0x06}, + {DRVPOWERCTRL, 0x06, 0x06}, }; struct sc_reg_access sc_access_IS[] = { {LMUTE, 0x04, 0x06}, - {RMUTE, 0x04, 0x06} + {RMUTE, 0x04, 0x06}, + {DRVPOWERCTRL, 0x00, 0x06}, }; int retval = 0; @@ -512,17 +545,26 @@ static int nc_set_selected_output_dev(u8 value) if (retval) return retval; pr_debug("nc set selected output:%d\n", value); + mutex_lock(&snd_pmic_ops_nc.lock); switch (value) { case STEREO_HEADPHONE: + if (snd_pmic_ops_nc.pb_on) + sst_sc_reg_access(sc_access_HP+2, PMIC_WRITE, 1); retval = sst_sc_reg_access(sc_access_HP, PMIC_WRITE, 2); + nc_set_amp_power(0); break; + case MONO_EARPIECE: case INTERNAL_SPKR: - retval = sst_sc_reg_access(sc_access_IS, PMIC_WRITE, 2); + retval = sst_sc_reg_access(sc_access_IS, PMIC_WRITE, 3); + if (snd_pmic_ops_nc.pb_on) + nc_set_amp_power(1); break; default: pr_err("rcvd illegal request: %d\n", value); + mutex_unlock(&snd_pmic_ops_nc.lock); return -EINVAL; } + mutex_unlock(&snd_pmic_ops_nc.lock); return retval; } @@ -784,9 +826,8 @@ static int nc_set_vol(int dev_id, int value) case PMIC_SND_LEFT_PB_VOL: pr_debug("PMIC_SND_LEFT_HP_VOL %d\n", value); sc_access[0].value = -value; - sc_access[0].reg_addr = AUDIOLVOL; - sc_access[0].mask = - (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); + sc_access[0].reg_addr = HPLVOL; + sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4); entries = 1; break; @@ -794,15 +835,32 @@ static int nc_set_vol(int dev_id, int value) pr_debug("PMIC_SND_RIGHT_HP_VOL value %d\n", value); if (snd_pmic_ops_nc.num_channel == 1) { sc_access[0].value = 0x04; - sc_access[0].reg_addr = RMUTE; + sc_access[0].reg_addr = RMUTE; sc_access[0].mask = MASK2; } else { + sc_access[0].value = -value; + sc_access[0].reg_addr = HPRVOL; + sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4); + } + entries = 1; + break; + + case PMIC_SND_LEFT_MASTER_VOL: + pr_debug("PMIC_SND_LEFT_MASTER_VOL value %d\n", value); + sc_access[0].value = -value; + sc_access[0].reg_addr = AUDIOLVOL; + sc_access[0].mask = + (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); + entries = 1; + break; + + case PMIC_SND_RIGHT_MASTER_VOL: + pr_debug("PMIC_SND_RIGHT_MASTER_VOL value %d\n", value); sc_access[0].value = -value; - sc_access[0].reg_addr = AUDIORVOL; + sc_access[0].reg_addr = AUDIORVOL; sc_access[0].mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); entries = 1; - } break; default: @@ -831,7 +889,7 @@ static int nc_set_selected_input_dev(u8 value) pr_debug("Selecting AMIC\n"); sc_access[0].reg_addr = 0x107; sc_access[0].value = 0x40; - sc_access[0].mask = MASK6|MASK4|MASK3|MASK1|MASK0; + sc_access[0].mask = MASK6|MASK3|MASK1|MASK0; sc_access[1].reg_addr = 0x10a; sc_access[1].value = 0x40; sc_access[1].mask = MASK6; @@ -846,9 +904,9 @@ static int nc_set_selected_input_dev(u8 value) case HS_MIC: pr_debug("Selecting HS_MIC\n"); - sc_access[0].reg_addr = 0x107; - sc_access[0].mask = MASK6|MASK4|MASK3|MASK1|MASK0; - sc_access[0].value = 0x10; + sc_access[0].reg_addr = MICCTRL; + sc_access[0].mask = MASK6|MASK3|MASK1|MASK0; + sc_access[0].value = 0x00; sc_access[1].reg_addr = 0x109; sc_access[1].mask = MASK6; sc_access[1].value = 0x40; @@ -858,13 +916,16 @@ static int nc_set_selected_input_dev(u8 value) sc_access[3].reg_addr = 0x105; sc_access[3].value = 0x40; sc_access[3].mask = MASK6; - num_val = 4; + sc_access[4].reg_addr = ADCSAMPLERATE; + sc_access[4].mask = MASK7|MASK6|MASK5|MASK4|MASK3; + sc_access[4].value = 0xc8; + num_val = 5; break; case DMIC: pr_debug("DMIC\n"); - sc_access[0].reg_addr = 0x107; - sc_access[0].mask = MASK6|MASK4|MASK3|MASK1|MASK0; + sc_access[0].reg_addr = MICCTRL; + sc_access[0].mask = MASK6|MASK3|MASK1|MASK0; sc_access[0].value = 0x0B; sc_access[1].reg_addr = 0x105; sc_access[1].value = 0x80; @@ -872,12 +933,12 @@ static int nc_set_selected_input_dev(u8 value) sc_access[2].reg_addr = 0x10a; sc_access[2].value = 0x40; sc_access[2].mask = MASK6; - sc_access[3].reg_addr = 0x109; + sc_access[3].reg_addr = LILSEL; sc_access[3].mask = MASK6; sc_access[3].value = 0x00; - sc_access[4].reg_addr = 0x104; - sc_access[4].value = 0x3C; - sc_access[4].mask = 0xff; + sc_access[4].reg_addr = ADCSAMPLERATE; + sc_access[4].mask = MASK7|MASK6|MASK5|MASK4|MASK3; + sc_access[4].value = 0x33; num_val = 5; break; default: @@ -964,18 +1025,30 @@ static int nc_get_vol(int dev_id, int *value) mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5); break; - case PMIC_SND_RIGHT_PB_VOL: - pr_debug("GET_VOLUME_PMIC_LEFT_HP_VOL\n"); + case PMIC_SND_LEFT_MASTER_VOL: + pr_debug("GET_VOLUME_PMIC_LEFT_MASTER_VOL\n"); sc_access.reg_addr = AUDIOLVOL; mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); break; - case PMIC_SND_LEFT_PB_VOL: - pr_debug("GET_VOLUME_PMIC_RIGHT_HP_VOL\n"); + case PMIC_SND_RIGHT_MASTER_VOL: + pr_debug("GET_VOLUME_PMIC_RIGHT_MASTER_VOL\n"); sc_access.reg_addr = AUDIORVOL; mask = (MASK0|MASK1|MASK2|MASK3|MASK4|MASK5|MASK6); break; + case PMIC_SND_RIGHT_PB_VOL: + pr_debug("GET_VOLUME_PMIC_RIGHT_HP_VOL\n"); + sc_access.reg_addr = HPRVOL; + mask = (MASK0|MASK1|MASK2|MASK3|MASK4); + break; + + case PMIC_SND_LEFT_PB_VOL: + pr_debug("GET_VOLUME_PMIC_LEFT_HP_VOL\n"); + sc_access.reg_addr = HPLVOL; + mask = (MASK0|MASK1|MASK2|MASK3|MASK4); + break; + default: return -EINVAL; @@ -987,7 +1060,81 @@ static int nc_get_vol(int dev_id, int *value) return retval; } +static void hp_automute(enum snd_jack_types type, int present) +{ + u8 in = DMIC; + u8 out = INTERNAL_SPKR; + if (present) { + if (type == SND_JACK_HEADSET) + in = HS_MIC; + out = STEREO_HEADPHONE; + } + nc_set_selected_input_dev(in); + nc_set_selected_output_dev(out); +} + +static void nc_pmic_irq_cb(void *cb_data, u8 intsts) +{ + u8 value = 0; + struct mad_jack *mjack = NULL; + unsigned int present = 0, jack_event_flag = 0, buttonpressflag = 0; + struct snd_intelmad *intelmaddata = cb_data; + struct sc_reg_access sc_access_read = {0,}; + + sc_access_read.reg_addr = 0x132; + sst_sc_reg_access(&sc_access_read, PMIC_READ, 1); + value = (sc_access_read.value); + pr_debug("value returned = 0x%x\n", value); + + mjack = &intelmaddata->jack[0]; + if (intsts & 0x1) { + pr_debug("SST DBG:MAD headset detected\n"); + /* send headset detect/undetect */ + present = (value == 0x1) ? 1 : 0; + jack_event_flag = 1; + mjack->jack.type = SND_JACK_HEADSET; + hp_automute(SND_JACK_HEADSET, present); + } + + if (intsts & 0x2) { + pr_debug(":MAD headphone detected\n"); + /* send headphone detect/undetect */ + present = (value == 0x2) ? 1 : 0; + jack_event_flag = 1; + mjack->jack.type = SND_JACK_HEADPHONE; + hp_automute(SND_JACK_HEADPHONE, present); + } + + if (intsts & 0x4) { + pr_debug("MAD short push detected\n"); + /* send short push */ + present = 1; + jack_event_flag = 1; + buttonpressflag = 1; + mjack->jack.type = MID_JACK_HS_SHORT_PRESS; + } + + if (intsts & 0x8) { + pr_debug(":MAD long push detected\n"); + /* send long push */ + present = 1; + jack_event_flag = 1; + buttonpressflag = 1; + mjack->jack.type = MID_JACK_HS_LONG_PRESS; + } + + if (jack_event_flag) + sst_mad_send_jack_report(&mjack->jack, + buttonpressflag, present); +} +static int nc_jack_enable(void) +{ + return 0; +} + struct snd_pmic_ops snd_pmic_ops_nc = { + .input_dev_id = DMIC, + .output_dev_id = INTERNAL_SPKR, .set_input_dev = nc_set_selected_input_dev, .set_output_dev = nc_set_selected_output_dev, .set_mute = nc_set_mute, @@ -1003,5 +1150,7 @@ struct snd_pmic_ops snd_pmic_ops_nc = { .power_up_pmic_cp = nc_power_up_cp, .power_down_pmic_pb = nc_power_down_pb, .power_down_pmic_cp = nc_power_down_cp, - .power_down_pmic = nc_power_down, + .power_down_pmic = nc_power_down, + .pmic_irq_cb = nc_pmic_irq_cb, + .pmic_jack_enable = nc_jack_enable, }; diff --git a/drivers/staging/intel_sst/jack.h b/drivers/staging/intel_sst/jack.h deleted file mode 100644 index 9a6e483ddeba..000000000000 --- a/drivers/staging/intel_sst/jack.h +++ /dev/null @@ -1,10 +0,0 @@ -/* Temporary staging glue */ - -#include <sound/jack.h> - -/* These want adding to jack.h as enum entries once approved */ - -#define SND_JACK_HS_SHORT_PRESS (SND_JACK_HEADSET | 0x0020) -#define SND_JACK_HS_LONG_PRESS (SND_JACK_HEADSET | 0x0040) - - |