diff options
Diffstat (limited to 'drivers')
614 files changed, 24956 insertions, 7589 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index eb67aadf2ee0..2545cf95e8db 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_USB_PHY) += usb/ obj-$(CONFIG_USB) += usb/ obj-$(CONFIG_PCI) += usb/ obj-$(CONFIG_USB_GADGET) += usb/ +obj-$(CONFIG_OF) += usb/ obj-$(CONFIG_SERIO) += input/serio/ obj-$(CONFIG_GAMEPORT) += input/gameport/ obj-$(CONFIG_INPUT) += input/ diff --git a/drivers/android/binder.c b/drivers/android/binder.c index d1490be45c67..0c3cf182e351 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3778,7 +3778,9 @@ static void binder_deferred_release(struct binder_proc *proc) page_count++; } kfree(proc->pages); + preempt_enable_no_resched(); vfree(proc->buffer); + preempt_disable(); } put_task_struct(proc->tsk); diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 212ca2eee257..a1696e1d199f 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -295,6 +295,7 @@ static void fw_free_buf(struct firmware_buf *buf) { struct firmware_cache *fwc = buf->fwc; if (!fwc) { + kfree_const(buf->fw_id); kfree(buf); return; } @@ -310,7 +311,8 @@ static const char * const fw_path[] = { "/lib/firmware/updates/" UTS_RELEASE, "/lib/firmware/updates", "/lib/firmware/" UTS_RELEASE, - "/lib/firmware" + "/lib/firmware", + "/lib64/firmware" }; /* diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 3252429f96af..3a20dc594338 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -889,13 +889,13 @@ int __pm_runtime_idle(struct device *dev, int rpmflags) unsigned long flags; int retval; - might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe); - if (rpmflags & RPM_GET_PUT) { if (!atomic_dec_and_test(&dev->power.usage_count)) return 0; } + might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe); + spin_lock_irqsave(&dev->power.lock, flags); retval = rpm_idle(dev, rpmflags); spin_unlock_irqrestore(&dev->power.lock, flags); @@ -921,13 +921,13 @@ int __pm_runtime_suspend(struct device *dev, int rpmflags) unsigned long flags; int retval; - might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe); - if (rpmflags & RPM_GET_PUT) { if (!atomic_dec_and_test(&dev->power.usage_count)) return 0; } + might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe); + spin_lock_irqsave(&dev->power.lock, flags); retval = rpm_suspend(dev, rpmflags); spin_unlock_irqrestore(&dev->power.lock, flags); @@ -952,7 +952,8 @@ int __pm_runtime_resume(struct device *dev, int rpmflags) unsigned long flags; int retval; - might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe); + might_sleep_if(!(rpmflags & RPM_ASYNC) && !dev->power.irq_safe && + dev->power.runtime_status != RPM_ACTIVE); if (rpmflags & RPM_GET_PUT) atomic_inc(&dev->power.usage_count); diff --git a/drivers/bluetooth/bluetooth-power.c b/drivers/bluetooth/bluetooth-power.c index 59245ba320f6..99c18e3d66d7 100644 --- a/drivers/bluetooth/bluetooth-power.c +++ b/drivers/bluetooth/bluetooth-power.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2010, 2013-2016 The Linux Foundation. All rights reserved. +/* Copyright (c) 2009-2010, 2013-2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -682,7 +682,7 @@ int bt_register_slimdev(struct device *dev) static long bt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - int ret, pwr_cntrl = 0; + int ret = 0, pwr_cntrl = 0; switch (cmd) { case BT_CMD_SLIM_TEST: diff --git a/drivers/bluetooth/btfm_slim_codec.c b/drivers/bluetooth/btfm_slim_codec.c index 05da1fb1f975..96be0e2f9183 100644 --- a/drivers/bluetooth/btfm_slim_codec.c +++ b/drivers/bluetooth/btfm_slim_codec.c @@ -26,6 +26,9 @@ #include <sound/tlv.h> #include <btfm_slim.h> +static int bt_soc_enable_status; + + static int btfm_slim_codec_write(struct snd_soc_codec *codec, unsigned int reg, unsigned int value) { @@ -38,8 +41,31 @@ static unsigned int btfm_slim_codec_read(struct snd_soc_codec *codec, return 0; } +static int bt_soc_status_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = bt_soc_enable_status; + return 1; +} + +static int bt_soc_status_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 1; +} + +static const struct snd_kcontrol_new status_controls[] = { + SOC_SINGLE_EXT("BT SOC status", 0, 0, 1, 0, + bt_soc_status_get, + bt_soc_status_put) + +}; + + static int btfm_slim_codec_probe(struct snd_soc_codec *codec) { + snd_soc_add_codec_controls(codec, status_controls, + ARRAY_SIZE(status_controls)); return 0; } @@ -130,6 +156,7 @@ int btfm_slim_dai_prepare(struct snd_pcm_substream *substream, struct btfmslim *btfmslim = dai->dev->platform_data; struct btfmslim_ch *ch; uint8_t rxport, grp = false, nchan = 1; + bt_soc_enable_status = 0; BTFMSLIM_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name, dai->id, dai->rate); @@ -171,6 +198,10 @@ int btfm_slim_dai_prepare(struct snd_pcm_substream *substream, } ret = btfm_slim_enable_ch(btfmslim, ch, rxport, dai->rate, grp, nchan); + + /* save the enable channel status */ + if (ret == 0) + bt_soc_enable_status = 1; return ret; } @@ -296,9 +327,9 @@ static int btfm_slim_dai_get_channel_map(struct snd_soc_dai *dai, unsigned int *tx_num, unsigned int *tx_slot, unsigned int *rx_num, unsigned int *rx_slot) { - int i, ret = -EINVAL, *slot, j = 0, num = 1; + int i, ret = -EINVAL, *slot = NULL, j = 0, num = 1; struct btfmslim *btfmslim = dai->dev->platform_data; - struct btfmslim_ch *ch; + struct btfmslim_ch *ch = NULL; if (!btfmslim) return ret; diff --git a/drivers/bluetooth/btfm_slim_wcn3990.c b/drivers/bluetooth/btfm_slim_wcn3990.c index 77e2973e023c..0c4e0b3d5c2e 100644 --- a/drivers/bluetooth/btfm_slim_wcn3990.c +++ b/drivers/bluetooth/btfm_slim_wcn3990.c @@ -88,12 +88,12 @@ int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num, BTFMSLIM_DBG("port(%d) enable(%d)", port_num, enable); if (rxport) { - if (enable && btfmslim->sample_rate == 48000) { - /* For A2DP Rx */ + if (enable) { + /* For SCO Rx, A2DP Rx */ reg_val = 0x1; port_bit = port_num - 0x10; reg = CHRK_SB_PGD_RX_PORTn_MULTI_CHNL_0(port_bit); - BTFMSLIM_DBG("writing reg_val (%d) to reg(%x) for A2DP", + BTFMSLIM_DBG("writing reg_val (%d) to reg(%x)", reg_val, reg); ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD); if (ret) { diff --git a/drivers/bluetooth/hci_bcm.c b/drivers/bluetooth/hci_bcm.c index cb852cc750b7..f9b569ef3dd7 100644 --- a/drivers/bluetooth/hci_bcm.c +++ b/drivers/bluetooth/hci_bcm.c @@ -287,6 +287,9 @@ static int bcm_open(struct hci_uart *hu) hu->priv = bcm; + if (!hu->tty->dev) + goto out; + mutex_lock(&bcm_device_lock); list_for_each(p, &bcm_device_list) { struct bcm_device *dev = list_entry(p, struct bcm_device, list); @@ -307,7 +310,7 @@ static int bcm_open(struct hci_uart *hu) } mutex_unlock(&bcm_device_lock); - +out: return 0; } diff --git a/drivers/bluetooth/hci_intel.c b/drivers/bluetooth/hci_intel.c index b9065506a847..0c63fce0c1e0 100644 --- a/drivers/bluetooth/hci_intel.c +++ b/drivers/bluetooth/hci_intel.c @@ -307,6 +307,9 @@ static int intel_set_power(struct hci_uart *hu, bool powered) struct list_head *p; int err = -ENODEV; + if (!hu->tty->dev) + return err; + mutex_lock(&intel_device_list_lock); list_for_each(p, &intel_device_list) { @@ -379,6 +382,9 @@ static void intel_busy_work(struct work_struct *work) struct intel_data *intel = container_of(work, struct intel_data, busy_work); + if (!intel->hu->tty->dev) + return; + /* Link is busy, delay the suspend */ mutex_lock(&intel_device_list_lock); list_for_each(p, &intel_device_list) { @@ -913,6 +919,8 @@ done: list_for_each(p, &intel_device_list) { struct intel_device *dev = list_entry(p, struct intel_device, list); + if (!hu->tty->dev) + break; if (hu->tty->dev->parent == dev->pdev->dev.parent) { if (device_may_wakeup(&dev->pdev->dev)) idev = dev; @@ -1094,6 +1102,9 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb) BT_DBG("hu %p skb %p", hu, skb); + if (!hu->tty->dev) + goto out_enqueue; + /* Be sure our controller is resumed and potential LPM transaction * completed before enqueuing any packet. */ @@ -1110,7 +1121,7 @@ static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb) } } mutex_unlock(&intel_device_list_lock); - +out_enqueue: skb_queue_tail(&intel->txq, skb); return 0; diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 14c833691194..ed0226131b90 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -670,7 +670,8 @@ static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd, unsigned attr, init_dma_attrs(&attrs); dma_set_attr(DMA_ATTR_EXEC_MAPPING, &attrs); - if (map->attr & FASTRPC_ATTR_NON_COHERENT) + if ((map->attr & FASTRPC_ATTR_NON_COHERENT) || + (sess->smmu.coherent && map->uncached)) dma_set_attr(DMA_ATTR_FORCE_NON_COHERENT, &attrs); else if (map->attr & FASTRPC_ATTR_COHERENT) @@ -1686,6 +1687,9 @@ static int fastrpc_init_process(struct fastrpc_file *fl, int namelen; int pageslen; } inbuf; + + if (!init->filelen) + goto bail; VERIFY(err, proc_name = kzalloc(init->filelen, GFP_KERNEL)); if (err) goto bail; @@ -1694,7 +1698,7 @@ static int fastrpc_init_process(struct fastrpc_file *fl, if (err) goto bail; inbuf.pgid = current->tgid; - inbuf.namelen = strlen(proc_name)+1; + inbuf.namelen = init->filelen; inbuf.pageslen = 0; if (!me->staticpd_flags) { inbuf.pageslen = 1; diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index fb45af9c49d3..196e87b61705 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.c @@ -2910,6 +2910,8 @@ int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry) new_entry->num_buffers = 1; break; } + + new_entry->buffers = NULL; new_entry->real_time = MODE_REALTIME; new_entry->in_service = 0; INIT_LIST_HEAD(&new_entry->list_write_buf); @@ -2983,7 +2985,8 @@ int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry) fail_alloc: if (new_entry) { - for (i = 0; i < new_entry->num_buffers; i++) { + for (i = 0; ((i < new_entry->num_buffers) && + new_entry->buffers); i++) { proc_buf = &new_entry->buffers[i]; if (proc_buf) { mutex_destroy(&proc_buf->health_mutex); diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c index 382717bad828..20e617ed0770 100644 --- a/drivers/char/diag/diag_masks.c +++ b/drivers/char/diag/diag_masks.c @@ -28,7 +28,8 @@ #define DIAG_SET_FEATURE_MASK(x) (feature_bytes[(x)/8] |= (1 << (x & 0x7))) #define diag_check_update(x) \ - (!info || (info && (info->peripheral_mask & MD_PERIPHERAL_MASK(x)))) \ + (!info || (info && (info->peripheral_mask & MD_PERIPHERAL_MASK(x))) \ + || (info && (info->peripheral_mask & MD_PERIPHERAL_PD_MASK(x)))) \ struct diag_mask_info msg_mask; struct diag_mask_info msg_bt_mask; @@ -60,7 +61,8 @@ static const struct diag_ssid_range_t msg_mask_tbl[] = { { .ssid_first = MSG_SSID_21, .ssid_last = MSG_SSID_21_LAST }, { .ssid_first = MSG_SSID_22, .ssid_last = MSG_SSID_22_LAST }, { .ssid_first = MSG_SSID_23, .ssid_last = MSG_SSID_23_LAST }, - { .ssid_first = MSG_SSID_24, .ssid_last = MSG_SSID_24_LAST } + { .ssid_first = MSG_SSID_24, .ssid_last = MSG_SSID_24_LAST }, + { .ssid_first = MSG_SSID_25, .ssid_last = MSG_SSID_25_LAST } }; static int diag_apps_responds(void) @@ -89,7 +91,7 @@ static void diag_send_log_mask_update(uint8_t peripheral, int equip_id) int err = 0; int send_once = 0; int header_len = sizeof(struct diag_ctrl_log_mask); - uint8_t *buf = NULL; + uint8_t *buf = NULL, upd = 0; uint8_t *temp = NULL; uint32_t mask_size = 0; struct diag_ctrl_log_mask ctrl_pkt; @@ -106,11 +108,25 @@ static void diag_send_log_mask_update(uint8_t peripheral, int equip_id) return; } - if (driver->md_session_mask != 0 && - driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) - mask_info = driver->md_session_map[peripheral]->log_mask; - else + if (driver->md_session_mask != 0) { + if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) { + if (driver->md_session_map[peripheral]) + mask_info = + driver->md_session_map[peripheral]->log_mask; + } else if (driver->md_session_mask & + MD_PERIPHERAL_PD_MASK(peripheral)) { + upd = diag_mask_to_pd_value(driver->md_session_mask); + if (upd && driver->md_session_map[upd]) + mask_info = + driver->md_session_map[upd]->log_mask; + } else { + DIAG_LOG(DIAG_DEBUG_MASKS, + "asking for mask update with unknown session mask\n"); + return; + } + } else { mask_info = &log_mask; + } if (!mask_info || !mask_info->ptr || !mask_info->update_buf) return; @@ -195,7 +211,7 @@ static void diag_send_log_mask_update(uint8_t peripheral, int equip_id) static void diag_send_event_mask_update(uint8_t peripheral) { - uint8_t *buf = NULL; + uint8_t *buf = NULL, upd = 0; uint8_t *temp = NULL; struct diag_ctrl_event_mask header; struct diag_mask_info *mask_info = NULL; @@ -220,11 +236,25 @@ static void diag_send_event_mask_update(uint8_t peripheral) return; } - if (driver->md_session_mask != 0 && - (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral))) - mask_info = driver->md_session_map[peripheral]->event_mask; - else + if (driver->md_session_mask != 0) { + if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) { + if (driver->md_session_map[peripheral]) + mask_info = + driver->md_session_map[peripheral]->event_mask; + } else if (driver->md_session_mask & + MD_PERIPHERAL_PD_MASK(peripheral)) { + upd = diag_mask_to_pd_value(driver->md_session_mask); + if (upd && driver->md_session_map[upd]) + mask_info = + driver->md_session_map[upd]->event_mask; + } else { + DIAG_LOG(DIAG_DEBUG_MASKS, + "asking for mask update with unknown session mask\n"); + return; + } + } else { mask_info = &event_mask; + } if (!mask_info || !mask_info->ptr || !mask_info->update_buf) return; @@ -284,7 +314,7 @@ static void diag_send_msg_mask_update(uint8_t peripheral, int first, int last) int err = 0; int header_len = sizeof(struct diag_ctrl_msg_mask); int temp_len = 0; - uint8_t *buf = NULL; + uint8_t *buf = NULL, upd = 0; uint8_t *temp = NULL; uint32_t mask_size = 0; struct diag_mask_info *mask_info = NULL; @@ -301,11 +331,25 @@ static void diag_send_msg_mask_update(uint8_t peripheral, int first, int last) return; } - if (driver->md_session_mask != 0 && - (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral))) - mask_info = driver->md_session_map[peripheral]->msg_mask; - else + if (driver->md_session_mask != 0) { + if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) { + if (driver->md_session_map[peripheral]) + mask_info = + driver->md_session_map[peripheral]->msg_mask; + } else if (driver->md_session_mask & + MD_PERIPHERAL_PD_MASK(peripheral)) { + upd = diag_mask_to_pd_value(driver->md_session_mask); + if (upd && driver->md_session_map[upd]) + mask_info = + driver->md_session_map[upd]->msg_mask; + } else { + DIAG_LOG(DIAG_DEBUG_MASKS, + "asking for mask update with unknown session mask\n"); + return; + } + } else { mask_info = &msg_mask; + } if (!mask_info || !mask_info->ptr || !mask_info->update_buf) return; diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c index a5d92c51cc0b..06b83f5230bf 100644 --- a/drivers/char/diag/diag_memorydevice.c +++ b/drivers/char/diag/diag_memorydevice.c @@ -254,8 +254,6 @@ int diag_md_copy_to_user(char __user *buf, int *pret, size_t buf_size, struct diag_md_session_t *session_info = NULL; struct pid *pid_struct = NULL; - mutex_lock(&driver->diagfwd_untag_mutex); - for (i = 0; i < NUM_DIAG_MD_DEV && !err; i++) { ch = &diag_md[i]; for (j = 0; j < ch->num_tbl_entries && !err; j++) { @@ -360,17 +358,11 @@ drop_data: err = copy_to_user(buf + sizeof(int), (void *)&num_data, sizeof(int)); - } else { - DIAG_LOG(DIAG_DEBUG_PERIPHERALS, - "diag: md_session_map[%d] with pid = %d Exited..\n", - peripheral, driver->md_session_map[peripheral]->pid); } diag_ws_on_copy_complete(DIAG_WS_MUX); if (drain_again) chk_logging_wakeup(); - mutex_unlock(&driver->diagfwd_untag_mutex); - return err; } diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h index 73296b573436..92cf24dcab5e 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.h @@ -235,6 +235,10 @@ #define MD_PERIPHERAL_MASK(x) (1 << x) +#define MD_PERIPHERAL_PD_MASK(x) \ + ((x == PERIPHERAL_MODEM) ? (1 << UPD_WLAN) : \ + ((x == PERIPHERAL_LPASS) ? (1 << UPD_AUDIO | 1 << UPD_SENSORS) : 0))\ + /* * Number of stm processors includes all the peripherals and * apps.Added 1 below to indicate apps @@ -543,7 +547,6 @@ struct diagchar_dev { struct mutex cmd_reg_mutex; uint32_t cmd_reg_count; struct mutex diagfwd_channel_mutex[NUM_PERIPHERALS]; - struct mutex diagfwd_untag_mutex; /* Sizes that reflect memory pool sizes */ unsigned int poolsize; unsigned int poolsize_hdlc; @@ -609,12 +612,6 @@ struct diagchar_dev { int pd_logging_mode[NUM_UPD]; int pd_session_clear[NUM_UPD]; int num_pd_session; - int cpd_len_1[NUM_PERIPHERALS]; - int cpd_len_2[NUM_PERIPHERALS]; - int upd_len_1_a[NUM_PERIPHERALS]; - int upd_len_1_b[NUM_PERIPHERALS]; - int upd_len_2_a; - int upd_len_2_b; int mask_check; uint32_t md_session_mask; uint8_t md_session_mode; @@ -675,6 +672,7 @@ void diag_cmd_remove_reg_by_proc(int proc); int diag_cmd_chk_polling(struct diag_cmd_reg_entry_t *entry); int diag_mask_param(void); void diag_clear_masks(struct diag_md_session_t *info); +uint8_t diag_mask_to_pd_value(uint32_t peripheral_mask); void diag_record_stats(int type, int flag); diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index 5c1094b48e92..0bc23199b92e 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -403,6 +403,30 @@ static uint32_t diag_translate_kernel_to_user_mask(uint32_t peripheral_mask) ret |= DIAG_CON_UPD_SENSORS; return ret; } + +uint8_t diag_mask_to_pd_value(uint32_t peripheral_mask) +{ + uint8_t upd = 0; + uint32_t pd_mask = 0; + + pd_mask = diag_translate_kernel_to_user_mask(peripheral_mask); + switch (pd_mask) { + case DIAG_CON_UPD_WLAN: + upd = UPD_WLAN; + break; + case DIAG_CON_UPD_AUDIO: + upd = UPD_AUDIO; + break; + case DIAG_CON_UPD_SENSORS: + upd = UPD_SENSORS; + break; + default: + DIAG_LOG(DIAG_DEBUG_MASKS, + "asking for mask update with no pd mask set\n"); + } + return upd; +} + int diag_mask_param(void) { return diag_mask_clear_param; @@ -457,20 +481,21 @@ static void diag_close_logging_process(const int pid) params.req_mode = USB_MODE; params.mode_param = 0; + params.pd_mask = 0; params.peripheral_mask = diag_translate_kernel_to_user_mask(session_mask); - for (i = UPD_WLAN; i < NUM_MD_SESSIONS; i++) { - if (session_mask & - MD_PERIPHERAL_MASK(i)) { + if (driver->num_pd_session > 0) { + for (i = UPD_WLAN; ((i < NUM_MD_SESSIONS) && + (session_mask & MD_PERIPHERAL_MASK(i))); + i++) { j = i - UPD_WLAN; driver->pd_session_clear[j] = 1; driver->pd_logging_mode[j] = 0; driver->num_pd_session -= 1; params.pd_mask = diag_translate_kernel_to_user_mask(session_mask); - } else - params.pd_mask = 0; + } } diag_switch_logging(¶ms); @@ -1588,7 +1613,7 @@ static uint32_t diag_translate_mask(uint32_t peripheral_mask) static int diag_switch_logging(struct diag_logging_mode_param_t *param) { - int new_mode, i; + int new_mode, i = 0; int curr_mode; int err = 0; uint8_t do_switch = 1; @@ -1629,6 +1654,8 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param) diag_mux->mux_mask)) { DIAG_LOG(DIAG_DEBUG_USERSPACE, "diag_fr: User PD is already logging onto active peripheral logging\n"); + i = upd - UPD_WLAN; + driver->pd_session_clear[i] = 0; return -EINVAL; } peripheral_mask = @@ -1638,8 +1665,8 @@ static int diag_switch_logging(struct diag_logging_mode_param_t *param) if (!driver->pd_session_clear[i]) { driver->pd_logging_mode[i] = 1; driver->num_pd_session += 1; - driver->pd_session_clear[i] = 0; } + driver->pd_session_clear[i] = 0; } else { peripheral_mask = diag_translate_mask(param->peripheral_mask); @@ -3599,7 +3626,6 @@ static int __init diagchar_init(void) mutex_init(&driver->msg_mask_lock); for (i = 0; i < NUM_PERIPHERALS; i++) mutex_init(&driver->diagfwd_channel_mutex[i]); - mutex_init(&driver->diagfwd_untag_mutex); init_waitqueue_head(&driver->wait_q); INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn); INIT_WORK(&(driver->update_user_clients), diff --git a/drivers/char/diag/diagfwd_glink.h b/drivers/char/diag/diagfwd_glink.h index a84fa4edfca0..6cad44522ab6 100644 --- a/drivers/char/diag/diagfwd_glink.h +++ b/drivers/char/diag/diagfwd_glink.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and diff --git a/drivers/char/diag/diagfwd_mhi.c b/drivers/char/diag/diagfwd_mhi.c index 8b0e1f32bdc5..03133a5a89aa 100644 --- a/drivers/char/diag/diagfwd_mhi.c +++ b/drivers/char/diag/diagfwd_mhi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -553,6 +553,8 @@ static void mhi_notifier(struct mhi_cb_info *cb_info) struct mhi_result *result = NULL; struct diag_mhi_ch_t *ch = NULL; void *buf = NULL; + struct diag_mhi_info *mhi_info = NULL; + unsigned long flags; if (!cb_info) return; @@ -604,13 +606,6 @@ static void mhi_notifier(struct mhi_cb_info *cb_info) queue_work(diag_mhi[index].mhi_wq, &(diag_mhi[index].open_work)); break; - case MHI_CB_MHI_DISABLED: - DIAG_LOG(DIAG_DEBUG_BRIDGE, - "received mhi disabled notifiation port: %d ch: %d\n", - index, ch->type); - atomic_set(&(ch->opened), 0); - __mhi_close(&diag_mhi[index], CHANNELS_CLOSED); - break; case MHI_CB_XFER: /* * If the channel is a read channel, this is a read @@ -637,6 +632,24 @@ static void mhi_notifier(struct mhi_cb_info *cb_info) result->bytes_xferd, diag_mhi[index].id); break; + case MHI_CB_MHI_DISABLED: + case MHI_CB_SYS_ERROR: + case MHI_CB_MHI_SHUTDOWN: + DIAG_LOG(DIAG_DEBUG_BRIDGE, + "received mhi link down cb: %d port: %d ch: %d\n", + cb_info->cb_reason, index, ch->type); + mhi_info = &diag_mhi[index]; + if (!mhi_info->enabled) + return; + spin_lock_irqsave(&mhi_info->lock, flags); + mhi_info->enabled = 0; + spin_unlock_irqrestore(&mhi_info->lock, flags); + atomic_set(&(mhi_info->read_ch.opened), 0); + atomic_set(&(mhi_info->write_ch.opened), 0); + flush_workqueue(mhi_info->mhi_wq); + mhi_buf_tbl_clear(mhi_info); + diag_remote_dev_close(mhi_info->dev_id); + break; default: pr_err("diag: In %s, invalid cb reason 0x%x\n", __func__, cb_info->cb_reason); diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c index e86dc8292bf0..e209039bed5a 100644 --- a/drivers/char/diag/diagfwd_peripheral.c +++ b/drivers/char/diag/diagfwd_peripheral.c @@ -363,7 +363,6 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, if (driver->feature[peripheral].encode_hdlc && driver->feature[peripheral].untag_header && driver->peripheral_untag[peripheral]) { - mutex_lock(&driver->diagfwd_untag_mutex); temp_buf_cpd = buf; temp_buf_main = buf; if (fwd_info->buf_1 && @@ -463,10 +462,10 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, if (peripheral == PERIPHERAL_LPASS && fwd_info->type == TYPE_DATA && len_upd_2) { if (flag_buf_1) { - driver->upd_len_2_a = len_upd_2; + fwd_info->upd_len_2_a = len_upd_2; temp_ptr_upd = fwd_info->buf_upd_2_a; } else { - driver->upd_len_2_b = len_upd_2; + fwd_info->upd_len_2_b = len_upd_2; temp_ptr_upd = fwd_info->buf_upd_2_b; } temp_ptr_upd->ctxt &= 0x00FFFFFF; @@ -477,17 +476,17 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, temp_ptr_upd, len_upd_2); } else { if (flag_buf_1) - driver->upd_len_2_a = 0; + fwd_info->upd_len_2_a = 0; if (flag_buf_2) - driver->upd_len_2_b = 0; + fwd_info->upd_len_2_b = 0; } if (fwd_info->type == TYPE_DATA && len_upd_1) { if (flag_buf_1) { - driver->upd_len_1_a[peripheral] = + fwd_info->upd_len_1_a = len_upd_1; temp_ptr_upd = fwd_info->buf_upd_1_a; } else { - driver->upd_len_1_b[peripheral] = + fwd_info->upd_len_1_b = len_upd_1; temp_ptr_upd = fwd_info->buf_upd_1_b; } @@ -499,15 +498,15 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, temp_ptr_upd, len_upd_1); } else { if (flag_buf_1) - driver->upd_len_1_a[peripheral] = 0; + fwd_info->upd_len_1_a = 0; if (flag_buf_2) - driver->upd_len_1_b[peripheral] = 0; + fwd_info->upd_len_1_b = 0; } if (len_cpd) { if (flag_buf_1) - driver->cpd_len_1[peripheral] = len_cpd; + fwd_info->cpd_len_1 = len_cpd; else - driver->cpd_len_2[peripheral] = len_cpd; + fwd_info->cpd_len_2 = len_cpd; temp_ptr_cpd->ctxt &= 0x00FFFFFF; temp_ptr_cpd->ctxt |= (SET_PD_CTXT(ctxt_cpd)); @@ -515,11 +514,10 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, temp_ptr_cpd, len_cpd); } else { if (flag_buf_1) - driver->cpd_len_1[peripheral] = 0; + fwd_info->cpd_len_1 = 0; if (flag_buf_2) - driver->cpd_len_2[peripheral] = 0; + fwd_info->cpd_len_2 = 0; } - mutex_unlock(&driver->diagfwd_untag_mutex); return; } else { diagfwd_data_read_done(fwd_info, buf, len); @@ -527,7 +525,6 @@ static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info, } end: diag_ws_release(); - mutex_unlock(&driver->diagfwd_untag_mutex); if (temp_ptr_cpd) { diagfwd_write_done(fwd_info->peripheral, fwd_info->type, GET_BUF_NUM(temp_ptr_cpd->ctxt)); @@ -759,6 +756,12 @@ int diagfwd_peripheral_init(void) fwd_info->inited = 1; fwd_info->read_bytes = 0; fwd_info->write_bytes = 0; + fwd_info->cpd_len_1 = 0; + fwd_info->cpd_len_2 = 0; + fwd_info->upd_len_1_a = 0; + fwd_info->upd_len_1_b = 0; + fwd_info->upd_len_2_a = 0; + fwd_info->upd_len_2_a = 0; mutex_init(&fwd_info->buf_mutex); mutex_init(&fwd_info->data_mutex); spin_lock_init(&fwd_info->write_buf_lock); @@ -775,6 +778,12 @@ int diagfwd_peripheral_init(void) fwd_info->ch_open = 0; fwd_info->read_bytes = 0; fwd_info->write_bytes = 0; + fwd_info->cpd_len_1 = 0; + fwd_info->cpd_len_2 = 0; + fwd_info->upd_len_1_a = 0; + fwd_info->upd_len_1_b = 0; + fwd_info->upd_len_2_a = 0; + fwd_info->upd_len_2_a = 0; spin_lock_init(&fwd_info->write_buf_lock); mutex_init(&fwd_info->buf_mutex); mutex_init(&fwd_info->data_mutex); @@ -1273,11 +1282,11 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt) if (ctxt == 1 && fwd_info->buf_1) { /* Buffer 1 for core PD is freed */ atomic_set(&fwd_info->buf_1->in_busy, 0); - driver->cpd_len_1[peripheral] = 0; + fwd_info->cpd_len_1 = 0; } else if (ctxt == 2 && fwd_info->buf_2) { /* Buffer 2 for core PD is freed */ atomic_set(&fwd_info->buf_2->in_busy, 0); - driver->cpd_len_2[peripheral] = 0; + fwd_info->cpd_len_2 = 0; } else if (ctxt == 3 && fwd_info->buf_upd_1_a) { /* Buffer 1 for user pd 1 is freed */ atomic_set(&fwd_info->buf_upd_1_a->in_busy, 0); @@ -1286,17 +1295,17 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt) /* if not data in cpd and other user pd * free the core pd buffer for LPASS */ - if (!driver->cpd_len_1[PERIPHERAL_LPASS] && - !driver->upd_len_2_a) + if (!fwd_info->cpd_len_1 && + !fwd_info->upd_len_2_a) atomic_set(&fwd_info->buf_1->in_busy, 0); } else { /* if not data in cpd * free the core pd buffer for MPSS */ - if (!driver->cpd_len_1[PERIPHERAL_MODEM]) + if (!fwd_info->cpd_len_1) atomic_set(&fwd_info->buf_1->in_busy, 0); } - driver->upd_len_1_a[peripheral] = 0; + fwd_info->upd_len_1_a = 0; } else if (ctxt == 4 && fwd_info->buf_upd_1_b) { /* Buffer 2 for user pd 1 is freed */ @@ -1305,17 +1314,17 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt) /* if not data in cpd and other user pd * free the core pd buffer for LPASS */ - if (!driver->cpd_len_2[peripheral] && - !driver->upd_len_2_b) + if (!fwd_info->cpd_len_2 && + !fwd_info->upd_len_2_b) atomic_set(&fwd_info->buf_2->in_busy, 0); } else { /* if not data in cpd * free the core pd buffer for MPSS */ - if (!driver->cpd_len_2[PERIPHERAL_MODEM]) + if (!fwd_info->cpd_len_2) atomic_set(&fwd_info->buf_2->in_busy, 0); } - driver->upd_len_1_b[peripheral] = 0; + fwd_info->upd_len_1_b = 0; } else if (ctxt == 5 && fwd_info->buf_upd_2_a) { /* Buffer 1 for user pd 2 is freed */ @@ -1323,11 +1332,11 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt) /* if not data in cpd and other user pd * free the core pd buffer for LPASS */ - if (!driver->cpd_len_1[PERIPHERAL_LPASS] && - !driver->upd_len_1_a[PERIPHERAL_LPASS]) + if (!fwd_info->cpd_len_1 && + !fwd_info->upd_len_1_a) atomic_set(&fwd_info->buf_1->in_busy, 0); - driver->upd_len_2_a = 0; + fwd_info->upd_len_2_a = 0; } else if (ctxt == 6 && fwd_info->buf_upd_2_b) { /* Buffer 2 for user pd 2 is freed */ @@ -1335,11 +1344,11 @@ void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt) /* if not data in cpd and other user pd * free the core pd buffer for LPASS */ - if (!driver->cpd_len_2[PERIPHERAL_LPASS] && - !driver->upd_len_1_b[PERIPHERAL_LPASS]) + if (!fwd_info->cpd_len_2 && + !fwd_info->upd_len_1_b) atomic_set(&fwd_info->buf_2->in_busy, 0); - driver->upd_len_2_b = 0; + fwd_info->upd_len_2_b = 0; } else pr_err("diag: In %s, invalid ctxt %d\n", __func__, ctxt); diff --git a/drivers/char/diag/diagfwd_peripheral.h b/drivers/char/diag/diagfwd_peripheral.h index 760f139ff428..037eeebdeb35 100644 --- a/drivers/char/diag/diagfwd_peripheral.h +++ b/drivers/char/diag/diagfwd_peripheral.h @@ -83,6 +83,12 @@ struct diagfwd_info { struct diagfwd_buf_t *buf_upd_2_a; struct diagfwd_buf_t *buf_upd_2_b; struct diagfwd_buf_t *buf_ptr[NUM_WRITE_BUFFERS]; + int cpd_len_1; + int cpd_len_2; + int upd_len_1_a; + int upd_len_1_b; + int upd_len_2_a; + int upd_len_2_b; struct diag_peripheral_ops *p_ops; struct diag_channel_ops *c_ops; }; diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 90e624662257..0d83cfb9708f 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -888,6 +888,7 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, * for details on the intricacies of this. */ int left; + unsigned char *data_to_send; ssif_inc_stat(ssif_info, sent_messages_parts); @@ -896,6 +897,7 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, left = 32; /* Length byte. */ ssif_info->multi_data[ssif_info->multi_pos] = left; + data_to_send = ssif_info->multi_data + ssif_info->multi_pos; ssif_info->multi_pos += left; if (left < 32) /* @@ -909,7 +911,7 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, rv = ssif_i2c_send(ssif_info, msg_written_handler, I2C_SMBUS_WRITE, SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE, - ssif_info->multi_data + ssif_info->multi_pos, + data_to_send, I2C_SMBUS_BLOCK_DATA); if (rv < 0) { /* request failed, just return the error. */ diff --git a/drivers/char/lp.c b/drivers/char/lp.c index c4094c4e22c1..34ef474a3923 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -859,7 +859,11 @@ static int __init lp_setup (char *str) } else if (!strcmp(str, "auto")) { parport_nr[0] = LP_PARPORT_AUTO; } else if (!strcmp(str, "none")) { - parport_nr[parport_ptr++] = LP_PARPORT_NONE; + if (parport_ptr < LP_NO) + parport_nr[parport_ptr++] = LP_PARPORT_NONE; + else + printk(KERN_INFO "lp: too many ports, %s ignored.\n", + str); } else if (!strcmp(str, "reset")) { reset = 1; } diff --git a/drivers/char/mem.c b/drivers/char/mem.c index e901463d4972..2898d19fadf5 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -343,6 +343,11 @@ static const struct vm_operations_struct mmap_mem_ops = { static int mmap_mem(struct file *file, struct vm_area_struct *vma) { size_t size = vma->vm_end - vma->vm_start; + phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT; + + /* It's illegal to wrap around the end of the physical address space. */ + if (offset + (phys_addr_t)size - 1 < offset) + return -EINVAL; if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) return -EINVAL; diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index fc061f7c2bd1..a7de8ae185a5 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -374,7 +374,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf, rc = write_sync_reg(SCR_HOST_TO_READER_START, dev); if (rc <= 0) { - DEBUGP(5, dev, "write_sync_reg c=%.2Zx\n", rc); + DEBUGP(5, dev, "write_sync_reg c=%.2zx\n", rc); DEBUGP(2, dev, "<- cm4040_write (failed)\n"); if (rc == -ERESTARTSYS) return rc; @@ -387,7 +387,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf, for (i = 0; i < bytes_to_write; i++) { rc = wait_for_bulk_out_ready(dev); if (rc <= 0) { - DEBUGP(5, dev, "wait_for_bulk_out_ready rc=%.2Zx\n", + DEBUGP(5, dev, "wait_for_bulk_out_ready rc=%.2zx\n", rc); DEBUGP(2, dev, "<- cm4040_write (failed)\n"); if (rc == -ERESTARTSYS) @@ -403,7 +403,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf, rc = write_sync_reg(SCR_HOST_TO_READER_DONE, dev); if (rc <= 0) { - DEBUGP(5, dev, "write_sync_reg c=%.2Zx\n", rc); + DEBUGP(5, dev, "write_sync_reg c=%.2zx\n", rc); DEBUGP(2, dev, "<- cm4040_write (failed)\n"); if (rc == -ERESTARTSYS) return rc; diff --git a/drivers/char/random.c b/drivers/char/random.c index d93dfebae0bb..1822472dffab 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1798,13 +1798,15 @@ int random_int_secret_init(void) return 0; } +static DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash) + __aligned(sizeof(unsigned long)); + /* * Get a random word for internal kernel use only. Similar to urandom but * with the goal of minimal entropy pool depletion. As a result, the random * value is not cryptographically secure but for several uses the cost of * depleting entropy is too high */ -static DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash); unsigned int get_random_int(void) { __u32 *hash; diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 2b21398c3adc..35308dfff754 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -118,8 +118,7 @@ static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count) memcpy_fromio(buf, priv->rsp, 6); expected = be32_to_cpup((__be32 *) &buf[2]); - - if (expected > count) + if (expected > count || expected < 6) return -EIO; memcpy_fromio(&buf[6], &priv->rsp[6], expected - 6); diff --git a/drivers/clk/msm/clock-local2.c b/drivers/clk/msm/clock-local2.c index adb07cdb7e8d..076ead6aaf34 100644 --- a/drivers/clk/msm/clock-local2.c +++ b/drivers/clk/msm/clock-local2.c @@ -1339,34 +1339,6 @@ static struct frac_entry frac_table_810m[] = { /* Link rate of 162M */ {0, 0}, }; -static bool is_same_rcg_config(struct rcg_clk *rcg, struct clk_freq_tbl *freq, - bool has_mnd) -{ - u32 cfg; - - /* RCG update pending */ - if (readl_relaxed(CMD_RCGR_REG(rcg)) & CMD_RCGR_CONFIG_DIRTY_MASK) - return false; - if (has_mnd) - if (readl_relaxed(M_REG(rcg)) != freq->m_val || - readl_relaxed(N_REG(rcg)) != freq->n_val || - readl_relaxed(D_REG(rcg)) != freq->d_val) - return false; - /* - * Both 0 and 1 represent same divider value in HW. - * Always use 0 to simplify comparison. - */ - if ((freq->div_src_val & CFG_RCGR_DIV_MASK) == 1) - freq->div_src_val &= ~CFG_RCGR_DIV_MASK; - cfg = readl_relaxed(CFG_RCGR_REG(rcg)); - if ((cfg & CFG_RCGR_DIV_MASK) == 1) - cfg &= ~CFG_RCGR_DIV_MASK; - if (cfg != freq->div_src_val) - return false; - - return true; -} - static int set_rate_edp_pixel(struct clk *clk, unsigned long rate) { struct rcg_clk *rcg = to_rcg_clk(clk); @@ -1404,8 +1376,7 @@ static int set_rate_edp_pixel(struct clk *clk, unsigned long rate) pixel_freq->d_val = ~frac->den; } spin_lock_irqsave(&local_clock_reg_lock, flags); - if (!is_same_rcg_config(rcg, pixel_freq, true)) - __set_rate_mnd(rcg, pixel_freq); + __set_rate_mnd(rcg, pixel_freq); spin_unlock_irqrestore(&local_clock_reg_lock, flags); return 0; } @@ -1466,8 +1437,7 @@ static int set_rate_byte(struct clk *clk, unsigned long rate) byte_freq->div_src_val |= BVAL(4, 0, div); spin_lock_irqsave(&local_clock_reg_lock, flags); - if (!is_same_rcg_config(rcg, byte_freq, false)) - __set_rate_hid(rcg, byte_freq); + __set_rate_hid(rcg, byte_freq); spin_unlock_irqrestore(&local_clock_reg_lock, flags); return 0; @@ -1788,8 +1758,7 @@ static int rcg_clk_set_rate_dp(struct clk *clk, unsigned long rate) } spin_lock_irqsave(&local_clock_reg_lock, flags); - if (!is_same_rcg_config(rcg, freq_tbl, true)) - __set_rate_mnd(rcg, freq_tbl); + __set_rate_mnd(rcg, freq_tbl); spin_unlock_irqrestore(&local_clock_reg_lock, flags); return 0; } diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c index 9d9aa61c480a..72a75873b810 100644 --- a/drivers/clk/msm/clock-osm.c +++ b/drivers/clk/msm/clock-osm.c @@ -606,6 +606,83 @@ static int clk_osm_acd_auto_local_write_reg(struct clk_osm *c, u32 mask) return 0; } +static int clk_osm_acd_init(struct clk_osm *c) +{ + + int rc = 0; + u32 auto_xfer_mask = 0; + + if (!c->acd_init) + return 0; + + c->acd_debugfs_addr = ACD_HW_VERSION; + + /* Program ACD tunable-length delay register */ + clk_osm_acd_master_write_reg(c, c->acd_td, ACDTD); + auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDTD); + + /* Program ACD control register */ + clk_osm_acd_master_write_reg(c, c->acd_cr, ACDCR); + auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDCR); + + /* Program ACD soft start control register */ + clk_osm_acd_master_write_reg(c, c->acd_sscr, ACDSSCR); + auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDSSCR); + + /* Program initial ACD external interface configuration register */ + clk_osm_acd_master_write_reg(c, c->acd_extint0_cfg, ACD_EXTINT_CFG); + auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_EXTINT_CFG); + + /* Program ACD auto-register transfer control register */ + clk_osm_acd_master_write_reg(c, c->acd_autoxfer_ctl, ACD_AUTOXFER_CTL); + + /* Ensure writes complete before transfers to local copy */ + clk_osm_acd_mb(c); + + /* Transfer master copies */ + rc = clk_osm_acd_auto_local_write_reg(c, auto_xfer_mask); + if (rc) + return rc; + + /* Switch CPUSS clock source to ACD clock */ + rc = clk_osm_acd_master_write_through_reg(c, ACD_GFMUX_CFG_SELECT, + ACD_GFMUX_CFG); + if (rc) + return rc; + + /* Program ACD_DCVS_SW */ + rc = clk_osm_acd_master_write_through_reg(c, + ACD_DCVS_SW_DCVS_IN_PRGR_SET, + ACD_DCVS_SW); + if (rc) + return rc; + + rc = clk_osm_acd_master_write_through_reg(c, + ACD_DCVS_SW_DCVS_IN_PRGR_CLEAR, + ACD_DCVS_SW); + if (rc) + return rc; + + udelay(1); + + /* Program final ACD external interface configuration register */ + rc = clk_osm_acd_master_write_through_reg(c, c->acd_extint1_cfg, + ACD_EXTINT_CFG); + if (rc) + return rc; + + /* + * ACDCR, ACDTD, ACDSSCR, ACD_EXTINT_CFG, ACD_GFMUX_CFG + * must be copied from master to local copy on PC exit. + */ + auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_GFMUX_CFG); + clk_osm_acd_master_write_reg(c, auto_xfer_mask, ACD_AUTOXFER_CFG); + + /* ACD has been initialized and enabled for this cluster */ + c->acd_init = false; + return 0; +} + static inline int clk_osm_count_ns(struct clk_osm *c, u64 nsec) { u64 temp; @@ -729,6 +806,17 @@ static int clk_osm_set_rate(struct clk *c, unsigned long rate) static int clk_osm_enable(struct clk *c) { struct clk_osm *cpuclk = to_clk_osm(c); + int rc; + + rc = clk_osm_acd_init(cpuclk); + if (rc) { + pr_err("Failed to initialize ACD for cluster %d, rc=%d\n", + cpuclk->cluster_num, rc); + return rc; + } + + /* Wait for 5 usecs before enabling OSM */ + udelay(5); clk_osm_write_reg(cpuclk, 1, ENABLE_REG); @@ -1541,8 +1629,8 @@ static int clk_osm_setup_hw_table(struct clk_osm *c) { struct osm_entry *entry = c->osm_table; int i; - u32 freq_val, volt_val, override_val, spare_val; - u32 table_entry_offset, last_spare, last_virtual_corner = 0; + u32 freq_val = 0, volt_val = 0, override_val = 0, spare_val = 0; + u32 table_entry_offset = 0, last_spare = 0, last_virtual_corner = 0; for (i = 0; i < OSM_TABLE_SIZE; i++) { if (i < c->num_entries) { @@ -2758,7 +2846,7 @@ static ssize_t debugfs_trace_method_get(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct clk_osm *c = file->private_data; - int len, rc; + int len = 0, rc; if (IS_ERR(file) || file == NULL) { pr_err("input error %ld\n", PTR_ERR(file)); @@ -3105,81 +3193,6 @@ static int clk_osm_panic_callback(struct notifier_block *nfb, return NOTIFY_OK; } -static int clk_osm_acd_init(struct clk_osm *c) -{ - - int rc = 0; - u32 auto_xfer_mask = 0; - - if (!c->acd_init) - return 0; - - c->acd_debugfs_addr = ACD_HW_VERSION; - - /* Program ACD tunable-length delay register */ - clk_osm_acd_master_write_reg(c, c->acd_td, ACDTD); - auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDTD); - - /* Program ACD control register */ - clk_osm_acd_master_write_reg(c, c->acd_cr, ACDCR); - auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDCR); - - /* Program ACD soft start control register */ - clk_osm_acd_master_write_reg(c, c->acd_sscr, ACDSSCR); - auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDSSCR); - - /* Program initial ACD external interface configuration register */ - clk_osm_acd_master_write_reg(c, c->acd_extint0_cfg, ACD_EXTINT_CFG); - auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_EXTINT_CFG); - - /* Program ACD auto-register transfer control register */ - clk_osm_acd_master_write_reg(c, c->acd_autoxfer_ctl, ACD_AUTOXFER_CTL); - - /* Ensure writes complete before transfers to local copy */ - clk_osm_acd_mb(c); - - /* Transfer master copies */ - rc = clk_osm_acd_auto_local_write_reg(c, auto_xfer_mask); - if (rc) - return rc; - - /* Switch CPUSS clock source to ACD clock */ - rc = clk_osm_acd_master_write_through_reg(c, ACD_GFMUX_CFG_SELECT, - ACD_GFMUX_CFG); - if (rc) - return rc; - - /* Program ACD_DCVS_SW */ - rc = clk_osm_acd_master_write_through_reg(c, - ACD_DCVS_SW_DCVS_IN_PRGR_SET, - ACD_DCVS_SW); - if (rc) - return rc; - - rc = clk_osm_acd_master_write_through_reg(c, - ACD_DCVS_SW_DCVS_IN_PRGR_CLEAR, - ACD_DCVS_SW); - if (rc) - return rc; - - udelay(1); - - /* Program final ACD external interface configuration register */ - rc = clk_osm_acd_master_write_through_reg(c, c->acd_extint1_cfg, - ACD_EXTINT_CFG); - if (rc) - return rc; - - /* - * ACDCR, ACDTD, ACDSSCR, ACD_EXTINT_CFG, ACD_GFMUX_CFG - * must be copied from master to local copy on PC exit. - */ - auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_GFMUX_CFG); - clk_osm_acd_master_write_reg(c, auto_xfer_mask, ACD_AUTOXFER_CFG); - - return 0; -} - static unsigned long init_rate = 300000000; static unsigned long osm_clk_init_rate = 200000000; @@ -3362,17 +3375,6 @@ static int cpu_clock_osm_driver_probe(struct platform_device *pdev) clk_osm_setup_cluster_pll(&perfcl_clk); } - rc = clk_osm_acd_init(&pwrcl_clk); - if (rc) { - pr_err("failed to initialize ACD for pwrcl, rc=%d\n", rc); - return rc; - } - rc = clk_osm_acd_init(&perfcl_clk); - if (rc) { - pr_err("failed to initialize ACD for perfcl, rc=%d\n", rc); - return rc; - } - spin_lock_init(&pwrcl_clk.lock); spin_lock_init(&perfcl_clk.lock); diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll-8998.c b/drivers/clk/msm/mdss/mdss-hdmi-pll-8998.c index c60c4864442f..c4215f30acce 100644 --- a/drivers/clk/msm/mdss/mdss-hdmi-pll-8998.c +++ b/drivers/clk/msm/mdss/mdss-hdmi-pll-8998.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,6 +29,10 @@ #define _W(x, y, z) MDSS_PLL_REG_W(x, y, z) #define _R(x, y) MDSS_PLL_REG_R(x, y) +/* CONSTANTS */ +#define HDMI_VERSION_8998_3_3 1 +#define HDMI_VERSION_8998_1_8 2 + /* PLL REGISTERS */ #define FREQ_UPDATE (0x008) #define BIAS_EN_CLKBUFLR_EN (0x034) @@ -277,7 +281,7 @@ find_optimal_index: } static int hdmi_8998_config_phy(unsigned long rate, - struct hdmi_8998_reg_cfg *cfg) + struct hdmi_8998_reg_cfg *cfg, u32 ver) { u64 const high_freq_bit_clk_threshold = 3400000000UL; u64 const dig_freq_bit_clk_threshold = 1500000000UL; @@ -359,6 +363,7 @@ static int hdmi_8998_config_phy(unsigned long rate, pr_debug("INTEGLOOP_GAIN = %llu\n", integloop_gain); pr_debug("CMP_RNG = %llu\n", cmp_rng); pr_debug("PLL_CMP = %llu\n", pll_cmp); + pr_debug("VER=%d\n", ver); cfg->svs_mode_clk_sel = (digclk_divsel & 0xFF); cfg->hsclk_sel = (0x20 | hsclk_sel); @@ -382,82 +387,105 @@ static int hdmi_8998_config_phy(unsigned long rate, cfg->core_clk_en = 0x2C; cfg->coreclk_div_mode0 = 0x5; cfg->phy_mode = (tmds_bclk_ratio ? 0x5 : 0x4); + /* V1P8_SEL */ + if (ver == HDMI_VERSION_8998_1_8) + cfg->phy_mode |= 1 << 4; cfg->ssc_en_center = 0x0; - if (bclk > high_freq_bit_clk_threshold) { - cfg->l0_tx_drv_lvl = 0xA; - cfg->l0_tx_emp_post1_lvl = 0x3; - cfg->l1_tx_drv_lvl = 0xA; - cfg->l1_tx_emp_post1_lvl = 0x3; - cfg->l2_tx_drv_lvl = 0xA; - cfg->l2_tx_emp_post1_lvl = 0x3; - cfg->l3_tx_drv_lvl = 0x8; - cfg->l3_tx_emp_post1_lvl = 0x3; - cfg->l0_pre_driver_1 = 0x0; - cfg->l0_pre_driver_2 = 0x1C; - cfg->l1_pre_driver_1 = 0x0; - cfg->l1_pre_driver_2 = 0x1C; - cfg->l2_pre_driver_1 = 0x0; - cfg->l2_pre_driver_2 = 0x1C; - cfg->l3_pre_driver_1 = 0x0; - cfg->l3_pre_driver_2 = 0x0; - } else if (bclk > dig_freq_bit_clk_threshold) { - cfg->l0_tx_drv_lvl = 0x9; - cfg->l0_tx_emp_post1_lvl = 0x3; - cfg->l1_tx_drv_lvl = 0x9; - cfg->l1_tx_emp_post1_lvl = 0x3; - cfg->l2_tx_drv_lvl = 0x9; - cfg->l2_tx_emp_post1_lvl = 0x3; - cfg->l3_tx_drv_lvl = 0x8; - cfg->l3_tx_emp_post1_lvl = 0x3; - cfg->l0_pre_driver_1 = 0x0; - cfg->l0_pre_driver_2 = 0x16; - cfg->l1_pre_driver_1 = 0x0; - cfg->l1_pre_driver_2 = 0x16; - cfg->l2_pre_driver_1 = 0x0; - cfg->l2_pre_driver_2 = 0x16; - cfg->l3_pre_driver_1 = 0x0; - cfg->l3_pre_driver_2 = 0x0; - } else if (bclk > mid_freq_bit_clk_threshold) { - cfg->l0_tx_drv_lvl = 0x9; - cfg->l0_tx_emp_post1_lvl = 0x3; - cfg->l1_tx_drv_lvl = 0x9; - cfg->l1_tx_emp_post1_lvl = 0x3; - cfg->l2_tx_drv_lvl = 0x9; - cfg->l2_tx_emp_post1_lvl = 0x3; - cfg->l3_tx_drv_lvl = 0x8; - cfg->l3_tx_emp_post1_lvl = 0x3; - cfg->l0_pre_driver_1 = 0x0; - cfg->l0_pre_driver_2 = 0x0E; - cfg->l1_pre_driver_1 = 0x0; - cfg->l1_pre_driver_2 = 0x0E; - cfg->l2_pre_driver_1 = 0x0; - cfg->l2_pre_driver_2 = 0x0E; - cfg->l3_pre_driver_1 = 0x0; - cfg->l3_pre_driver_2 = 0x0; + if (ver == HDMI_VERSION_8998_3_3) { + if (bclk > high_freq_bit_clk_threshold) { + cfg->l0_tx_drv_lvl = 0xA; + cfg->l0_tx_emp_post1_lvl = 0x3; + cfg->l1_tx_drv_lvl = 0xA; + cfg->l1_tx_emp_post1_lvl = 0x3; + cfg->l2_tx_drv_lvl = 0xA; + cfg->l2_tx_emp_post1_lvl = 0x3; + cfg->l3_tx_drv_lvl = 0x8; + cfg->l3_tx_emp_post1_lvl = 0x3; + cfg->l0_pre_driver_1 = 0x0; + cfg->l0_pre_driver_2 = 0x1C; + cfg->l1_pre_driver_1 = 0x0; + cfg->l1_pre_driver_2 = 0x1C; + cfg->l2_pre_driver_1 = 0x0; + cfg->l2_pre_driver_2 = 0x1C; + cfg->l3_pre_driver_1 = 0x0; + cfg->l3_pre_driver_2 = 0x0; + } else if (bclk > dig_freq_bit_clk_threshold) { + cfg->l0_tx_drv_lvl = 0x9; + cfg->l0_tx_emp_post1_lvl = 0x3; + cfg->l1_tx_drv_lvl = 0x9; + cfg->l1_tx_emp_post1_lvl = 0x3; + cfg->l2_tx_drv_lvl = 0x9; + cfg->l2_tx_emp_post1_lvl = 0x3; + cfg->l3_tx_drv_lvl = 0x8; + cfg->l3_tx_emp_post1_lvl = 0x3; + cfg->l0_pre_driver_1 = 0x0; + cfg->l0_pre_driver_2 = 0x16; + cfg->l1_pre_driver_1 = 0x0; + cfg->l1_pre_driver_2 = 0x16; + cfg->l2_pre_driver_1 = 0x0; + cfg->l2_pre_driver_2 = 0x16; + cfg->l3_pre_driver_1 = 0x0; + cfg->l3_pre_driver_2 = 0x0; + } else if (bclk > mid_freq_bit_clk_threshold) { + cfg->l0_tx_drv_lvl = 0x9; + cfg->l0_tx_emp_post1_lvl = 0x3; + cfg->l1_tx_drv_lvl = 0x9; + cfg->l1_tx_emp_post1_lvl = 0x3; + cfg->l2_tx_drv_lvl = 0x9; + cfg->l2_tx_emp_post1_lvl = 0x3; + cfg->l3_tx_drv_lvl = 0x8; + cfg->l3_tx_emp_post1_lvl = 0x3; + cfg->l0_pre_driver_1 = 0x0; + cfg->l0_pre_driver_2 = 0x0E; + cfg->l1_pre_driver_1 = 0x0; + cfg->l1_pre_driver_2 = 0x0E; + cfg->l2_pre_driver_1 = 0x0; + cfg->l2_pre_driver_2 = 0x0E; + cfg->l3_pre_driver_1 = 0x0; + cfg->l3_pre_driver_2 = 0x0; + } else { + cfg->l0_tx_drv_lvl = 0x0; + cfg->l0_tx_emp_post1_lvl = 0x0; + cfg->l1_tx_drv_lvl = 0x0; + cfg->l1_tx_emp_post1_lvl = 0x0; + cfg->l2_tx_drv_lvl = 0x0; + cfg->l2_tx_emp_post1_lvl = 0x0; + cfg->l3_tx_drv_lvl = 0x0; + cfg->l3_tx_emp_post1_lvl = 0x0; + cfg->l0_pre_driver_1 = 0x0; + cfg->l0_pre_driver_2 = 0x01; + cfg->l1_pre_driver_1 = 0x0; + cfg->l1_pre_driver_2 = 0x01; + cfg->l2_pre_driver_1 = 0x0; + cfg->l2_pre_driver_2 = 0x01; + cfg->l3_pre_driver_1 = 0x0; + cfg->l3_pre_driver_2 = 0x0; + } } else { - cfg->l0_tx_drv_lvl = 0x0; - cfg->l0_tx_emp_post1_lvl = 0x0; - cfg->l1_tx_drv_lvl = 0x0; - cfg->l1_tx_emp_post1_lvl = 0x0; - cfg->l2_tx_drv_lvl = 0x0; - cfg->l2_tx_emp_post1_lvl = 0x0; - cfg->l3_tx_drv_lvl = 0x0; + cfg->l0_tx_drv_lvl = 0xF; + cfg->l0_tx_emp_post1_lvl = 0x5; + cfg->l1_tx_drv_lvl = 0xF; + cfg->l1_tx_emp_post1_lvl = 0x2; + cfg->l2_tx_drv_lvl = 0xF; + cfg->l2_tx_emp_post1_lvl = 0x2; + cfg->l3_tx_drv_lvl = 0xF; cfg->l3_tx_emp_post1_lvl = 0x0; cfg->l0_pre_driver_1 = 0x0; - cfg->l0_pre_driver_2 = 0x01; + cfg->l0_pre_driver_2 = 0x1E; cfg->l1_pre_driver_1 = 0x0; - cfg->l1_pre_driver_2 = 0x01; + cfg->l1_pre_driver_2 = 0x1E; cfg->l2_pre_driver_1 = 0x0; - cfg->l2_pre_driver_2 = 0x01; + cfg->l2_pre_driver_2 = 0x1E; cfg->l3_pre_driver_1 = 0x0; - cfg->l3_pre_driver_2 = 0x0; + cfg->l3_pre_driver_2 = 0x10; } return rc; } -static int hdmi_8998_pll_set_clk_rate(struct clk *c, unsigned long rate) +static int hdmi_8998_pll_set_clk_rate(struct clk *c, unsigned long rate, + u32 ver) { int rc = 0; struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); @@ -465,7 +493,7 @@ static int hdmi_8998_pll_set_clk_rate(struct clk *c, unsigned long rate) struct hdmi_8998_reg_cfg cfg = {0}; void __iomem *phy = io->phy_base, *pll = io->pll_base; - rc = hdmi_8998_config_phy(rate, &cfg); + rc = hdmi_8998_config_phy(rate, &cfg, ver); if (rc) { pr_err("rate calculation failed\n, rc=%d", rc); return rc; @@ -699,7 +727,7 @@ static int hdmi_8998_vco_get_lock_range(struct clk *c, } static int hdmi_8998_vco_rate_atomic_update(struct clk *c, - unsigned long rate) + unsigned long rate, u32 ver) { struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); struct mdss_pll_resources *io = vco->priv; @@ -707,7 +735,7 @@ static int hdmi_8998_vco_rate_atomic_update(struct clk *c, struct hdmi_8998_reg_cfg cfg = {0}; int rc = 0; - rc = hdmi_8998_config_phy(rate, &cfg); + rc = hdmi_8998_config_phy(rate, &cfg, ver); if (rc) { pr_err("rate calculation failed\n, rc=%d", rc); goto end; @@ -728,7 +756,7 @@ end: return rc; } -static int hdmi_8998_vco_set_rate(struct clk *c, unsigned long rate) +static int hdmi_8998_vco_set_rate(struct clk *c, unsigned long rate, u32 ver) { struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); struct mdss_pll_resources *io = vco->priv; @@ -767,9 +795,9 @@ static int hdmi_8998_vco_set_rate(struct clk *c, unsigned long rate) set_power_dwn = 1; if (atomic_update) - rc = hdmi_8998_vco_rate_atomic_update(c, rate); + rc = hdmi_8998_vco_rate_atomic_update(c, rate, ver); else - rc = hdmi_8998_pll_set_clk_rate(c, rate); + rc = hdmi_8998_pll_set_clk_rate(c, rate, ver); if (rc) { pr_err("failed to set clk rate\n"); @@ -806,7 +834,7 @@ static long hdmi_8998_vco_round_rate(struct clk *c, unsigned long rate) return rrate; } -static int hdmi_8998_vco_prepare(struct clk *c) +static int hdmi_8998_vco_prepare(struct clk *c, u32 ver) { struct hdmi_pll_vco_clk *vco = to_hdmi_vco_clk(c); struct mdss_pll_resources *io = vco->priv; @@ -824,7 +852,7 @@ static int hdmi_8998_vco_prepare(struct clk *c) } if (!vco->rate_set && vco->rate) { - rc = hdmi_8998_pll_set_clk_rate(c, vco->rate); + rc = hdmi_8998_pll_set_clk_rate(c, vco->rate, ver); if (rc) { pr_err("set rate failed, rc=%d\n", rc); goto error; @@ -902,10 +930,38 @@ static enum handoff hdmi_8998_vco_handoff(struct clk *c) return ret; } -static struct clk_ops hdmi_8998_vco_clk_ops = { - .set_rate = hdmi_8998_vco_set_rate, +static int hdmi_8998_3p3_vco_set_rate(struct clk *c, unsigned long rate) +{ + return hdmi_8998_vco_set_rate(c, rate, HDMI_VERSION_8998_3_3); +} + +static int hdmi_8998_1p8_vco_set_rate(struct clk *c, unsigned long rate) +{ + return hdmi_8998_vco_set_rate(c, rate, HDMI_VERSION_8998_1_8); +} + +static int hdmi_8998_3p3_vco_prepare(struct clk *c) +{ + return hdmi_8998_vco_prepare(c, HDMI_VERSION_8998_3_3); +} + +static int hdmi_8998_1p8_vco_prepare(struct clk *c) +{ + return hdmi_8998_vco_prepare(c, HDMI_VERSION_8998_1_8); +} + +static struct clk_ops hdmi_8998_3p3_vco_clk_ops = { + .set_rate = hdmi_8998_3p3_vco_set_rate, + .round_rate = hdmi_8998_vco_round_rate, + .prepare = hdmi_8998_3p3_vco_prepare, + .unprepare = hdmi_8998_vco_unprepare, + .handoff = hdmi_8998_vco_handoff, +}; + +static struct clk_ops hdmi_8998_1p8_vco_clk_ops = { + .set_rate = hdmi_8998_1p8_vco_set_rate, .round_rate = hdmi_8998_vco_round_rate, - .prepare = hdmi_8998_vco_prepare, + .prepare = hdmi_8998_1p8_vco_prepare, .unprepare = hdmi_8998_vco_unprepare, .handoff = hdmi_8998_vco_handoff, }; @@ -915,7 +971,7 @@ static struct hdmi_pll_vco_clk hdmi_vco_clk = { .max_rate = HDMI_VCO_MAX_RATE_HZ, .c = { .dbg_name = "hdmi_8998_vco_clk", - .ops = &hdmi_8998_vco_clk_ops, + .ops = &hdmi_8998_3p3_vco_clk_ops, CLK_INIT(hdmi_vco_clk.c), }, }; @@ -925,7 +981,7 @@ static struct clk_lookup hdmipllcc_8998[] = { }; int hdmi_8998_pll_clock_register(struct platform_device *pdev, - struct mdss_pll_resources *pll_res) + struct mdss_pll_resources *pll_res, u32 ver) { int rc = 0; @@ -936,8 +992,20 @@ int hdmi_8998_pll_clock_register(struct platform_device *pdev, hdmi_vco_clk.priv = pll_res; + switch (ver) { + case HDMI_VERSION_8998_3_3: + hdmi_vco_clk.c.ops = &hdmi_8998_3p3_vco_clk_ops; + break; + case HDMI_VERSION_8998_1_8: + hdmi_vco_clk.c.ops = &hdmi_8998_1p8_vco_clk_ops; + break; + default: + hdmi_vco_clk.c.ops = &hdmi_8998_3p3_vco_clk_ops; + break; + }; + rc = of_msm_clock_register(pdev->dev.of_node, hdmipllcc_8998, - ARRAY_SIZE(hdmipllcc_8998)); + ARRAY_SIZE(hdmipllcc_8998)); if (rc) { pr_err("clock register failed, rc=%d\n", rc); return rc; @@ -945,3 +1013,17 @@ int hdmi_8998_pll_clock_register(struct platform_device *pdev, return rc; } + +int hdmi_8998_3p3_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + return hdmi_8998_pll_clock_register(pdev, pll_res, + HDMI_VERSION_8998_3_3); +} + +int hdmi_8998_1p8_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res) +{ + return hdmi_8998_pll_clock_register(pdev, pll_res, + HDMI_VERSION_8998_1_8); +} diff --git a/drivers/clk/msm/mdss/mdss-hdmi-pll.h b/drivers/clk/msm/mdss/mdss-hdmi-pll.h index 19f9b925644a..9e6a39481286 100644 --- a/drivers/clk/msm/mdss/mdss-hdmi-pll.h +++ b/drivers/clk/msm/mdss/mdss-hdmi-pll.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -45,17 +45,19 @@ int hdmi_20nm_pll_clock_register(struct platform_device *pdev, struct mdss_pll_resources *pll_res); int hdmi_8996_v1_pll_clock_register(struct platform_device *pdev, - struct mdss_pll_resources *pll_res); + struct mdss_pll_resources *pll_res); int hdmi_8996_v2_pll_clock_register(struct platform_device *pdev, - struct mdss_pll_resources *pll_res); + struct mdss_pll_resources *pll_res); int hdmi_8996_v3_pll_clock_register(struct platform_device *pdev, - struct mdss_pll_resources *pll_res); + struct mdss_pll_resources *pll_res); int hdmi_8996_v3_1p8_pll_clock_register(struct platform_device *pdev, - struct mdss_pll_resources *pll_res); + struct mdss_pll_resources *pll_res); -int hdmi_8998_pll_clock_register(struct platform_device *pdev, - struct mdss_pll_resources *pll_res); +int hdmi_8998_3p3_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); +int hdmi_8998_1p8_pll_clock_register(struct platform_device *pdev, + struct mdss_pll_resources *pll_res); #endif diff --git a/drivers/clk/msm/mdss/mdss-pll.c b/drivers/clk/msm/mdss/mdss-pll.c index 01ce2b1817f2..b5c98774ba92 100644 --- a/drivers/clk/msm/mdss/mdss-pll.c +++ b/drivers/clk/msm/mdss/mdss-pll.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -149,7 +149,9 @@ static int mdss_pll_resource_parse(struct platform_device *pdev, "qcom,mdss_hdmi_pll_8996_v3_1p8")) { pll_res->pll_interface_type = MDSS_HDMI_PLL_8996_V3_1_8; } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8998")) { - pll_res->pll_interface_type = MDSS_HDMI_PLL_8998; + pll_res->pll_interface_type = MDSS_HDMI_PLL_8998_3_3; + } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8998_1p8")) { + pll_res->pll_interface_type = MDSS_HDMI_PLL_8998_1_8; } else { goto err; } @@ -193,8 +195,11 @@ static int mdss_pll_clock_register(struct platform_device *pdev, case MDSS_HDMI_PLL_8996_V3_1_8: rc = hdmi_8996_v3_1p8_pll_clock_register(pdev, pll_res); break; - case MDSS_HDMI_PLL_8998: - rc = hdmi_8998_pll_clock_register(pdev, pll_res); + case MDSS_HDMI_PLL_8998_3_3: + rc = hdmi_8998_3p3_pll_clock_register(pdev, pll_res); + break; + case MDSS_HDMI_PLL_8998_1_8: + rc = hdmi_8998_1p8_pll_clock_register(pdev, pll_res); break; case MDSS_UNKNOWN_PLL: default: @@ -401,6 +406,7 @@ static const struct of_device_id mdss_pll_dt_match[] = { {.compatible = "qcom,mdss_hdmi_pll_8996_v3_1p8"}, {.compatible = "qcom,mdss_dp_pll_8998"}, {.compatible = "qcom,mdss_hdmi_pll_8998"}, + {.compatible = "qcom,mdss_hdmi_pll_8998_1p8"}, {} }; diff --git a/drivers/clk/msm/mdss/mdss-pll.h b/drivers/clk/msm/mdss/mdss-pll.h index 8fffaf30d4ec..0120d71f0daf 100644 --- a/drivers/clk/msm/mdss/mdss-pll.h +++ b/drivers/clk/msm/mdss/mdss-pll.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -37,7 +37,8 @@ enum { MDSS_HDMI_PLL_8996_V2, MDSS_HDMI_PLL_8996_V3, MDSS_HDMI_PLL_8996_V3_1_8, - MDSS_HDMI_PLL_8998, + MDSS_HDMI_PLL_8998_3_3, + MDSS_HDMI_PLL_8998_1_8, MDSS_UNKNOWN_PLL, }; diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 4c18181c047c..d3e88f40bdfd 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -9,6 +9,7 @@ clk-qcom-y += clk-rcg2.o clk-qcom-y += clk-branch.o clk-qcom-y += clk-regmap-divider.o clk-qcom-y += clk-regmap-mux.o +clk-qcom-y += clk-regmap-mux-div.o clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o clk-qcom-y += clk-hfpll.o clk-qcom-y += reset.o clk-voter.o diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c index 8bf45f572c5e..d99e13817a29 100644 --- a/drivers/clk/qcom/clk-cpu-osm.c +++ b/drivers/clk/qcom/clk-cpu-osm.c @@ -719,9 +719,22 @@ static int clk_osm_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } +static int clk_osm_acd_init(struct clk_osm *c); + static int clk_osm_enable(struct clk_hw *hw) { struct clk_osm *cpuclk = to_clk_osm(hw); + int rc; + + rc = clk_osm_acd_init(cpuclk); + if (rc) { + pr_err("Failed to initialize ACD for cluster %d, rc=%d\n", + cpuclk->cluster_num, rc); + return rc; + } + + /* Wait for 5 usecs before enabling OSM */ + udelay(5); clk_osm_write_reg(cpuclk, 1, ENABLE_REG); @@ -3272,17 +3285,6 @@ static int clk_cpu_osm_driver_probe(struct platform_device *pdev) clk_osm_setup_cluster_pll(&perfcl_clk); } - rc = clk_osm_acd_init(&pwrcl_clk); - if (rc) { - pr_err("failed to initialize ACD for pwrcl, rc=%d\n", rc); - return rc; - } - rc = clk_osm_acd_init(&perfcl_clk); - if (rc) { - pr_err("failed to initialize ACD for perfcl, rc=%d\n", rc); - return rc; - } - spin_lock_init(&pwrcl_clk.lock); spin_lock_init(&perfcl_clk.lock); diff --git a/drivers/clk/qcom/clk-regmap-mux-div.c b/drivers/clk/qcom/clk-regmap-mux-div.c new file mode 100644 index 000000000000..942a68e2a650 --- /dev/null +++ b/drivers/clk/qcom/clk-regmap-mux-div.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2015, Linaro Limited + * Copyright (c) 2014, 2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/regmap.h> + +#include "clk-regmap-mux-div.h" + +#define CMD_RCGR 0x0 +#define CMD_RCGR_UPDATE BIT(0) +#define CMD_RCGR_DIRTY_CFG BIT(4) +#define CMD_RCGR_ROOT_OFF BIT(31) +#define CFG_RCGR 0x4 + +#define to_clk_regmap_mux_div(_hw) \ + container_of(to_clk_regmap(_hw), struct clk_regmap_mux_div, clkr) + +int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div) +{ + int ret, count; + u32 val, mask; + const char *name = clk_hw_get_name(&md->clkr.hw); + + val = (div << md->hid_shift) | (src << md->src_shift); + mask = ((BIT(md->hid_width) - 1) << md->hid_shift) | + ((BIT(md->src_width) - 1) << md->src_shift); + + ret = regmap_update_bits(md->clkr.regmap, CFG_RCGR + md->reg_offset, + mask, val); + if (ret) + return ret; + + ret = regmap_update_bits(md->clkr.regmap, CMD_RCGR + md->reg_offset, + CMD_RCGR_UPDATE, CMD_RCGR_UPDATE); + if (ret) + return ret; + + /* Wait for update to take effect */ + for (count = 500; count > 0; count--) { + ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, + &val); + if (ret) + return ret; + if (!(val & CMD_RCGR_UPDATE)) + return 0; + udelay(1); + } + + pr_err("%s: RCG did not update its configuration", name); + return -EBUSY; +} + +int mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src, + u32 *div) +{ + int ret = 0; + u32 val, __div, __src; + const char *name = clk_hw_get_name(&md->clkr.hw); + + ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val); + if (ret) + return ret; + + if (val & CMD_RCGR_DIRTY_CFG) { + pr_err("%s: RCG configuration is pending\n", name); + return -EBUSY; + } + + ret = regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val); + if (ret) + return ret; + + __src = (val >> md->src_shift); + __src &= BIT(md->src_width) - 1; + *src = __src; + + __div = (val >> md->hid_shift); + __div &= BIT(md->hid_width) - 1; + *div = __div; + + return ret; +} + +static int mux_div_enable(struct clk_hw *hw) +{ + struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); + + return __mux_div_set_src_div(md, md->src, md->div); +} + +static inline bool is_better_rate(unsigned long req, unsigned long best, + unsigned long new) +{ + return (req <= new && new < best) || (best < req && best < new); +} + +static int mux_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); + unsigned int i, div, max_div; + unsigned long actual_rate, best_rate = 0; + unsigned long req_rate = req->rate; + + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i); + unsigned long parent_rate = clk_hw_get_rate(parent); + + max_div = BIT(md->hid_width) - 1; + for (div = 1; div < max_div; div++) { + parent_rate = mult_frac(req_rate, div, 2); + parent_rate = clk_hw_round_rate(parent, parent_rate); + actual_rate = mult_frac(parent_rate, 2, div); + + if (is_better_rate(req_rate, best_rate, actual_rate)) { + best_rate = actual_rate; + req->rate = best_rate; + req->best_parent_rate = parent_rate; + req->best_parent_hw = parent; + } + + if (actual_rate < req_rate || best_rate <= req_rate) + break; + } + } + + if (!best_rate) + return -EINVAL; + + return 0; +} + +static int __mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, + unsigned long prate, u32 src) +{ + struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); + int ret; + u32 div, max_div, best_src = 0, best_div = 0; + unsigned int i; + unsigned long actual_rate, best_rate = 0; + + for (i = 0; i < clk_hw_get_num_parents(hw); i++) { + struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i); + unsigned long parent_rate = clk_hw_get_rate(parent); + + max_div = BIT(md->hid_width) - 1; + for (div = 1; div < max_div; div++) { + parent_rate = mult_frac(rate, div, 2); + parent_rate = clk_hw_round_rate(parent, parent_rate); + actual_rate = mult_frac(parent_rate, 2, div); + + if (is_better_rate(rate, best_rate, actual_rate)) { + best_rate = actual_rate; + best_src = md->parent_map[i].cfg; + best_div = div - 1; + } + + if (actual_rate < rate || best_rate <= rate) + break; + } + } + + ret = __mux_div_set_src_div(md, best_src, best_div); + if (!ret) { + md->div = best_div; + md->src = best_src; + } + + return ret; +} + +static u8 mux_div_get_parent(struct clk_hw *hw) +{ + struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); + const char *name = clk_hw_get_name(hw); + u32 i, div, src = 0; + + mux_div_get_src_div(md, &src, &div); + + for (i = 0; i < clk_hw_get_num_parents(hw); i++) + if (src == md->parent_map[i].cfg) + return i; + + pr_err("%s: Can't find parent with src %d\n", name, src); + return 0; +} + +static int mux_div_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); + + return __mux_div_set_src_div(md, md->parent_map[index].cfg, md->div); +} + +static int mux_div_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long prate) +{ + struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); + + return __mux_div_set_rate_and_parent(hw, rate, prate, md->src); +} + +static int mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, + unsigned long prate, u8 index) +{ + struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); + + return __mux_div_set_rate_and_parent(hw, rate, prate, + md->parent_map[index].cfg); +} + +static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate) +{ + struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); + u32 div, src; + int i, num_parents = clk_hw_get_num_parents(hw); + const char *name = clk_hw_get_name(hw); + + mux_div_get_src_div(md, &src, &div); + for (i = 0; i < num_parents; i++) + if (src == md->parent_map[i].cfg) { + struct clk_hw *p = clk_hw_get_parent_by_index(hw, i); + unsigned long parent_rate = clk_hw_get_rate(p); + + return mult_frac(parent_rate, 2, div + 1); + } + + pr_err("%s: Can't find parent %d\n", name, src); + return 0; +} + +static void mux_div_disable(struct clk_hw *hw) +{ + struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw); + + __mux_div_set_src_div(md, md->safe_src, md->safe_div); +} + +const struct clk_ops clk_regmap_mux_div_ops = { + .enable = mux_div_enable, + .disable = mux_div_disable, + .get_parent = mux_div_get_parent, + .set_parent = mux_div_set_parent, + .set_rate = mux_div_set_rate, + .set_rate_and_parent = mux_div_set_rate_and_parent, + .determine_rate = mux_div_determine_rate, + .recalc_rate = mux_div_recalc_rate, +}; +EXPORT_SYMBOL_GPL(clk_regmap_mux_div_ops); diff --git a/drivers/clk/qcom/clk-regmap-mux-div.h b/drivers/clk/qcom/clk-regmap-mux-div.h new file mode 100644 index 000000000000..63a696a96033 --- /dev/null +++ b/drivers/clk/qcom/clk-regmap-mux-div.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015, Linaro Limited + * Copyright (c) 2014, 2017, The Linux Foundation. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#ifndef __QCOM_CLK_REGMAP_MUX_DIV_H__ +#define __QCOM_CLK_REGMAP_MUX_DIV_H__ + +#include <linux/clk-provider.h> +#include "clk-rcg.h" +#include "clk-regmap.h" + +/** + * struct mux_div_clk - combined mux/divider clock + * @reg_offset: offset of the mux/divider register + * @hid_width: number of bits in half integer divider + * @hid_shift: lowest bit of hid value field + * @src_width: number of bits in source select + * @src_shift: lowest bit of source select field + * @div: the divider raw configuration value + * @src: the mux index which will be used if the clock is enabled + * @safe_src: the safe source mux value we switch to, while the main PLL is + * reconfigured + * @safe_div: the safe divider value that we set, while the main PLL is + * reconfigured + * @safe_freq: When switching rates from A to B, the mux div clock will + * instead switch from A -> safe_freq -> B. This allows the + * mux_div clock to change rates while enabled, even if this + * behavior is not supported by the parent clocks. + * If changing the rate of parent A also causes the rate of + * parent B to change, then safe_freq must be defined. + * safe_freq is expected to have a source clock which is always + * on and runs at only one rate. + * @parent_map: pointer to parent_map struct + * @clkr: handle between common and hardware-specific interfaces + */ + +struct clk_regmap_mux_div { + u32 reg_offset; + u32 hid_width; + u32 hid_shift; + u32 src_width; + u32 src_shift; + u32 div; + u32 src; + u32 safe_src; + u32 safe_div; + unsigned long safe_freq; + const struct parent_map *parent_map; + struct clk_regmap clkr; +}; + +extern const struct clk_ops clk_regmap_mux_div_ops; +int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div); +int mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src, u32 *div); + +#endif diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 3b2f46bacd77..1dfd1765319b 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -120,6 +120,15 @@ config CPU_FREQ_DEFAULT_GOV_SCHED cpu frequency using CPU utilization estimates from the scheduler. +config CPU_FREQ_DEFAULT_GOV_SCHEDUTIL + bool "schedutil" + depends on SMP + select CPU_FREQ_GOV_SCHEDUTIL + select CPU_FREQ_GOV_PERFORMANCE + help + Use the 'schedutil' CPUFreq governor by default. If unsure, + have a look at the help section of that governor. The fallback + governor will be 'performance'. endchoice config CPU_FREQ_GOV_PERFORMANCE @@ -239,6 +248,23 @@ config CPU_FREQ_GOV_SCHED If in doubt, say N. +config CPU_FREQ_GOV_SCHEDUTIL + bool "'schedutil' cpufreq policy governor" + depends on CPU_FREQ && SMP + select CPU_FREQ_GOV_ATTR_SET + select IRQ_WORK + help + This governor makes decisions based on the utilization data provided + by the scheduler. It sets the CPU frequency to be proportional to + the utilization/capacity ratio coming from the scheduler. If the + utilization is frequency-invariant, the new frequency is also + proportional to the maximum available frequency. If that is not the + case, it is proportional to the current frequency of the CPU. The + frequency tipping point is at utilization/capacity equal to 80% in + both cases. + + If in doubt, say N. + comment "CPU frequency scaling drivers" config CPUFREQ_DT diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 4a2f914e0752..6d4a7aeb506d 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -1,5 +1,5 @@ # CPUfreq core -obj-$(CONFIG_CPU_FREQ) += cpufreq.o freq_table.o +obj-$(CONFIG_CPU_FREQ) += cpufreq.o freq_table.o cpufreq_governor_attr_set.o # CPUfreq stats obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index a0dba9beac05..5f0f983ce173 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -541,6 +541,38 @@ void cpufreq_freq_transition_end(struct cpufreq_policy *policy, } EXPORT_SYMBOL_GPL(cpufreq_freq_transition_end); +/** + * cpufreq_driver_resolve_freq - Map a target frequency to a driver-supported + * one. + * @target_freq: target frequency to resolve. + * + * The target to driver frequency mapping is cached in the policy. + * + * Return: Lowest driver-supported frequency greater than or equal to the + * given target_freq, subject to policy (min/max) and driver limitations. + */ +unsigned int cpufreq_driver_resolve_freq(struct cpufreq_policy *policy, + unsigned int target_freq) +{ + target_freq = clamp_val(target_freq, policy->min, policy->max); + policy->cached_target_freq = target_freq; + + if (cpufreq_driver->target_index) { + int idx, rv; + + rv = cpufreq_frequency_table_target(policy, policy->freq_table, + target_freq, + CPUFREQ_RELATION_L, + &idx); + if (rv) + return target_freq; + policy->cached_resolved_idx = idx; + return policy->freq_table[idx].frequency; + } + + return target_freq; +} +EXPORT_SYMBOL_GPL(cpufreq_driver_resolve_freq); /********************************************************************* * SYSFS INTERFACE * @@ -2536,6 +2568,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) if (!(cpufreq_driver->flags & CPUFREQ_STICKY) && list_empty(&cpufreq_policy_list)) { /* if all ->init() calls failed, unregister */ + ret = -ENODEV; pr_debug("%s: No CPU initialized for driver %s\n", __func__, driver_data->name); goto err_if_unreg; diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index 1fa1deb6e91f..c395f9198fd2 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -212,8 +212,8 @@ static ssize_t store_down_threshold(struct dbs_data *dbs_data, const char *buf, int ret; ret = sscanf(buf, "%u", &input); - /* cannot be lower than 11 otherwise freq will not fall */ - if (ret != 1 || input < 11 || input > 100 || + /* cannot be lower than 1 otherwise freq will not fall */ + if (ret != 1 || input < 1 || input > 100 || input >= cs_tuners->up_threshold) return -EINVAL; diff --git a/drivers/cpufreq/cpufreq_governor_attr_set.c b/drivers/cpufreq/cpufreq_governor_attr_set.c new file mode 100644 index 000000000000..52841f807a7e --- /dev/null +++ b/drivers/cpufreq/cpufreq_governor_attr_set.c @@ -0,0 +1,84 @@ +/* + * Abstract code for CPUFreq governor tunable sysfs attributes. + * + * Copyright (C) 2016, Intel Corporation + * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "cpufreq_governor.h" + +static inline struct gov_attr_set *to_gov_attr_set(struct kobject *kobj) +{ + return container_of(kobj, struct gov_attr_set, kobj); +} + +static inline struct governor_attr *to_gov_attr(struct attribute *attr) +{ + return container_of(attr, struct governor_attr, attr); +} + +static ssize_t governor_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct governor_attr *gattr = to_gov_attr(attr); + + return gattr->show(to_gov_attr_set(kobj), buf); +} + +static ssize_t governor_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct gov_attr_set *attr_set = to_gov_attr_set(kobj); + struct governor_attr *gattr = to_gov_attr(attr); + int ret; + + mutex_lock(&attr_set->update_lock); + ret = attr_set->usage_count ? gattr->store(attr_set, buf, count) : -EBUSY; + mutex_unlock(&attr_set->update_lock); + return ret; +} + +const struct sysfs_ops governor_sysfs_ops = { + .show = governor_show, + .store = governor_store, +}; +EXPORT_SYMBOL_GPL(governor_sysfs_ops); + +void gov_attr_set_init(struct gov_attr_set *attr_set, struct list_head *list_node) +{ + INIT_LIST_HEAD(&attr_set->policy_list); + mutex_init(&attr_set->update_lock); + attr_set->usage_count = 1; + list_add(list_node, &attr_set->policy_list); +} +EXPORT_SYMBOL_GPL(gov_attr_set_init); + +void gov_attr_set_get(struct gov_attr_set *attr_set, struct list_head *list_node) +{ + mutex_lock(&attr_set->update_lock); + attr_set->usage_count++; + list_add(list_node, &attr_set->policy_list); + mutex_unlock(&attr_set->update_lock); +} +EXPORT_SYMBOL_GPL(gov_attr_set_get); + +unsigned int gov_attr_set_put(struct gov_attr_set *attr_set, struct list_head *list_node) +{ + unsigned int count; + + mutex_lock(&attr_set->update_lock); + list_del(list_node); + count = --attr_set->usage_count; + mutex_unlock(&attr_set->update_lock); + if (count) + return count; + + kobject_put(&attr_set->kobj); + mutex_destroy(&attr_set->update_lock); + return 0; +} +EXPORT_SYMBOL_GPL(gov_attr_set_put); diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index b69e59eeeae1..584a1857624a 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -694,22 +694,21 @@ static int cpu_power_select(struct cpuidle_device *dev, int best_level = -1; uint32_t latency_us = pm_qos_request_for_cpu(PM_QOS_CPU_DMA_LATENCY, dev->cpu); - uint32_t sleep_us = - (uint32_t)(ktime_to_us(tick_nohz_get_sleep_length())); + s64 sleep_us = ktime_to_us(tick_nohz_get_sleep_length()); uint32_t modified_time_us = 0; uint32_t next_event_us = 0; int i, idx_restrict; uint32_t lvl_latency_us = 0; uint64_t predicted = 0; uint32_t htime = 0, idx_restrict_time = 0; - uint32_t next_wakeup_us = sleep_us; + uint32_t next_wakeup_us = (uint32_t)sleep_us; uint32_t *min_residency = get_per_cpu_min_residency(dev->cpu); uint32_t *max_residency = get_per_cpu_max_residency(dev->cpu); if (!cpu) return -EINVAL; - if (sleep_disabled && !cpu_isolated(dev->cpu)) + if ((sleep_disabled && !cpu_isolated(dev->cpu)) || sleep_us < 0) return 0; idx_restrict = cpu->nlevels + 1; @@ -750,8 +749,8 @@ static int cpu_power_select(struct cpuidle_device *dev, if (next_wakeup_us > max_residency[i]) { predicted = lpm_cpuidle_predict(dev, cpu, &idx_restrict, &idx_restrict_time); - if (predicted < min_residency[i]) - predicted = 0; + if (predicted && (predicted < min_residency[i])) + predicted = min_residency[i]; } else invalidate_predict_history(dev); } @@ -1119,10 +1118,14 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx, bool from_idle, int predicted) { struct lpm_cluster_level *level = &cluster->levels[idx]; + struct cpumask online_cpus; int ret, i; + cpumask_and(&online_cpus, &cluster->num_children_in_sync, + cpu_online_mask); + if (!cpumask_equal(&cluster->num_children_in_sync, &cluster->child_cpus) - || is_IPI_pending(&cluster->num_children_in_sync)) { + || is_IPI_pending(&online_cpus)) { return -EPERM; } diff --git a/drivers/crypto/msm/ice.c b/drivers/crypto/msm/ice.c index 4002a5b57250..f99a421c0ee4 100644 --- a/drivers/crypto/msm/ice.c +++ b/drivers/crypto/msm/ice.c @@ -125,6 +125,9 @@ static int qti_ice_setting_config(struct request *req, return -EPERM; } + if (!setting) + return -EINVAL; + if ((short)(crypto_data->key_index) >= 0) { memcpy(&setting->crypto_data, crypto_data, diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c index 893b0b6da6b8..b5c5dc035c66 100644 --- a/drivers/crypto/msm/qcrypto.c +++ b/drivers/crypto/msm/qcrypto.c @@ -437,6 +437,7 @@ struct qcrypto_cipher_req_ctx { u8 rfc4309_iv[QCRYPTO_MAX_IV_LENGTH]; unsigned int ivsize; int aead; + int ccmtype; /* default: 0, rfc4309: 1 */ struct scatterlist asg; /* Formatted associated data sg */ unsigned char *adata; /* Pointer to formatted assoc data */ enum qce_cipher_alg_enum alg; @@ -1897,9 +1898,8 @@ static int aead_ccm_set_msg_len(u8 *block, unsigned int msglen, int csize) return 0; } -static int qccrypto_set_aead_ccm_nonce(struct qce_req *qreq) +static int qccrypto_set_aead_ccm_nonce(struct qce_req *qreq, uint32_t assoclen) { - struct aead_request *areq = (struct aead_request *) qreq->areq; unsigned int i = ((unsigned int)qreq->iv[0]) + 1; memcpy(&qreq->nonce[0] , qreq->iv, qreq->ivsize); @@ -1908,7 +1908,7 @@ static int qccrypto_set_aead_ccm_nonce(struct qce_req *qreq) * NIST Special Publication 800-38C */ qreq->nonce[0] |= (8 * ((qreq->authsize - 2) / 2)); - if (areq->assoclen) + if (assoclen) qreq->nonce[0] |= 64; if (i > MAX_NONCE) @@ -2118,24 +2118,31 @@ static int _qcrypto_process_aead(struct crypto_engine *pengine, qreq.flags = cipher_ctx->flags; if (qreq.mode == QCE_MODE_CCM) { + uint32_t assoclen; + if (qreq.dir == QCE_ENCRYPT) qreq.cryptlen = req->cryptlen; else qreq.cryptlen = req->cryptlen - qreq.authsize; + + /* if rfc4309 ccm, adjust assoclen */ + assoclen = req->assoclen; + if (rctx->ccmtype) + assoclen -= 8; /* Get NONCE */ - ret = qccrypto_set_aead_ccm_nonce(&qreq); + ret = qccrypto_set_aead_ccm_nonce(&qreq, assoclen); if (ret) return ret; - if (req->assoclen) { - rctx->adata = kzalloc((req->assoclen + 0x64), + if (assoclen) { + rctx->adata = kzalloc((assoclen + 0x64), GFP_ATOMIC); if (!rctx->adata) return -ENOMEM; /* Format Associated data */ ret = qcrypto_aead_ccm_format_adata(&qreq, - req->assoclen, + assoclen, req->src, rctx->adata); } else { @@ -2592,6 +2599,7 @@ static int _qcrypto_aead_encrypt_aes_ccm(struct aead_request *req) rctx->dir = QCE_ENCRYPT; rctx->mode = QCE_MODE_CCM; rctx->iv = req->iv; + rctx->ccmtype = 0; pstat->aead_ccm_aes_enc++; return _qcrypto_queue_req(cp, ctx->pengine, &req->base); @@ -2606,6 +2614,8 @@ static int _qcrypto_aead_rfc4309_enc_aes_ccm(struct aead_request *req) pstat = &_qcrypto_stat; + if (req->assoclen != 16 && req->assoclen != 20) + return -EINVAL; rctx = aead_request_ctx(req); rctx->aead = 1; rctx->alg = CIPHER_ALG_AES; @@ -2615,6 +2625,7 @@ static int _qcrypto_aead_rfc4309_enc_aes_ccm(struct aead_request *req) rctx->rfc4309_iv[0] = 3; /* L -1 */ memcpy(&rctx->rfc4309_iv[1], ctx->ccm4309_nonce, 3); memcpy(&rctx->rfc4309_iv[4], req->iv, 8); + rctx->ccmtype = 1; rctx->iv = rctx->rfc4309_iv; pstat->aead_rfc4309_ccm_aes_enc++; return _qcrypto_queue_req(cp, ctx->pengine, &req->base); @@ -2922,6 +2933,7 @@ static int _qcrypto_aead_decrypt_aes_ccm(struct aead_request *req) rctx->dir = QCE_DECRYPT; rctx->mode = QCE_MODE_CCM; rctx->iv = req->iv; + rctx->ccmtype = 0; pstat->aead_ccm_aes_dec++; return _qcrypto_queue_req(cp, ctx->pengine, &req->base); @@ -2935,6 +2947,8 @@ static int _qcrypto_aead_rfc4309_dec_aes_ccm(struct aead_request *req) struct crypto_stat *pstat; pstat = &_qcrypto_stat; + if (req->assoclen != 16 && req->assoclen != 20) + return -EINVAL; rctx = aead_request_ctx(req); rctx->aead = 1; rctx->alg = CIPHER_ALG_AES; @@ -2944,6 +2958,7 @@ static int _qcrypto_aead_rfc4309_dec_aes_ccm(struct aead_request *req) rctx->rfc4309_iv[0] = 3; /* L -1 */ memcpy(&rctx->rfc4309_iv[1], ctx->ccm4309_nonce, 3); memcpy(&rctx->rfc4309_iv[4], req->iv, 8); + rctx->ccmtype = 1; rctx->iv = rctx->rfc4309_iv; pstat->aead_rfc4309_ccm_aes_dec++; return _qcrypto_queue_req(cp, ctx->pengine, &req->base); diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 57ff46284f15..c97336a2ba92 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -325,6 +325,8 @@ static int m2p_hw_setup(struct ep93xx_dma_chan *edmac) | M2P_CONTROL_ENABLE; m2p_set_control(edmac, control); + edmac->buffer = 0; + return 0; } diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c index b1bc945f008f..56410ea75ac5 100644 --- a/drivers/dma/sh/usb-dmac.c +++ b/drivers/dma/sh/usb-dmac.c @@ -117,7 +117,7 @@ struct usb_dmac { #define USB_DMASWR 0x0008 #define USB_DMASWR_SWR (1 << 0) #define USB_DMAOR 0x0060 -#define USB_DMAOR_AE (1 << 2) +#define USB_DMAOR_AE (1 << 1) #define USB_DMAOR_DME (1 << 0) #define USB_DMASAR 0x0000 diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 16a7b6816744..8decab2a9cce 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -597,7 +597,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) int idx, i; for (i = 0, idx = 0; idx <= index; i++) { - struct acpi_gpio_info info; + struct acpi_gpio_info info = {0, 0}; struct gpio_desc *desc; desc = acpi_get_gpiod_by_index(adev, NULL, i, &info); diff --git a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c index 57a2e347f04d..0f0094b58d1f 100644 --- a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c @@ -893,6 +893,12 @@ static bool ci_dpm_vblank_too_short(struct amdgpu_device *adev) u32 vblank_time = amdgpu_dpm_get_vblank_time(adev); u32 switch_limit = adev->mc.vram_type == AMDGPU_VRAM_TYPE_GDDR5 ? 450 : 300; + /* disable mclk switching if the refresh is >120Hz, even if the + * blanking period would allow it + */ + if (amdgpu_dpm_get_vrefresh(adev) > 120) + return true; + if (vblank_time < switch_limit) return true; else diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index 5b261adb4b69..3a25da4a6e60 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -1126,23 +1126,10 @@ static u32 dce_v10_0_latency_watermark(struct dce10_wm_params *wm) a.full = dfixed_const(available_bandwidth); b.full = dfixed_const(wm->num_heads); a.full = dfixed_div(a, b); + tmp = div_u64((u64) dmif_size * (u64) wm->disp_clk, mc_latency + 512); + tmp = min(dfixed_trunc(a), tmp); - b.full = dfixed_const(mc_latency + 512); - c.full = dfixed_const(wm->disp_clk); - b.full = dfixed_div(b, c); - - c.full = dfixed_const(dmif_size); - b.full = dfixed_div(c, b); - - tmp = min(dfixed_trunc(a), dfixed_trunc(b)); - - b.full = dfixed_const(1000); - c.full = dfixed_const(wm->disp_clk); - b.full = dfixed_div(c, b); - c.full = dfixed_const(wm->bytes_per_pixel); - b.full = dfixed_mul(b, c); - - lb_fill_bw = min(tmp, dfixed_trunc(b)); + lb_fill_bw = min(tmp, wm->disp_clk * wm->bytes_per_pixel / 1000); a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel); b.full = dfixed_const(1000); @@ -1250,14 +1237,14 @@ static void dce_v10_0_program_watermarks(struct amdgpu_device *adev, { struct drm_display_mode *mode = &amdgpu_crtc->base.mode; struct dce10_wm_params wm_low, wm_high; - u32 pixel_period; + u32 active_time; u32 line_time = 0; u32 latency_watermark_a = 0, latency_watermark_b = 0; u32 tmp, wm_mask, lb_vblank_lead_lines = 0; if (amdgpu_crtc->base.enabled && num_heads && mode) { - pixel_period = 1000000 / (u32)mode->clock; - line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535); + active_time = 1000000UL * (u32)mode->crtc_hdisplay / (u32)mode->clock; + line_time = min((u32) (1000000UL * (u32)mode->crtc_htotal / (u32)mode->clock), (u32)65535); /* watermark for high clocks */ if (adev->pm.dpm_enabled) { @@ -1272,7 +1259,7 @@ static void dce_v10_0_program_watermarks(struct amdgpu_device *adev, wm_high.disp_clk = mode->clock; wm_high.src_width = mode->crtc_hdisplay; - wm_high.active_time = mode->crtc_hdisplay * pixel_period; + wm_high.active_time = active_time; wm_high.blank_time = line_time - wm_high.active_time; wm_high.interlaced = false; if (mode->flags & DRM_MODE_FLAG_INTERLACE) @@ -1311,7 +1298,7 @@ static void dce_v10_0_program_watermarks(struct amdgpu_device *adev, wm_low.disp_clk = mode->clock; wm_low.src_width = mode->crtc_hdisplay; - wm_low.active_time = mode->crtc_hdisplay * pixel_period; + wm_low.active_time = active_time; wm_low.blank_time = line_time - wm_low.active_time; wm_low.interlaced = false; if (mode->flags & DRM_MODE_FLAG_INTERLACE) diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index 267749a94c5a..d6d3cda77762 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -1114,23 +1114,10 @@ static u32 dce_v11_0_latency_watermark(struct dce10_wm_params *wm) a.full = dfixed_const(available_bandwidth); b.full = dfixed_const(wm->num_heads); a.full = dfixed_div(a, b); + tmp = div_u64((u64) dmif_size * (u64) wm->disp_clk, mc_latency + 512); + tmp = min(dfixed_trunc(a), tmp); - b.full = dfixed_const(mc_latency + 512); - c.full = dfixed_const(wm->disp_clk); - b.full = dfixed_div(b, c); - - c.full = dfixed_const(dmif_size); - b.full = dfixed_div(c, b); - - tmp = min(dfixed_trunc(a), dfixed_trunc(b)); - - b.full = dfixed_const(1000); - c.full = dfixed_const(wm->disp_clk); - b.full = dfixed_div(c, b); - c.full = dfixed_const(wm->bytes_per_pixel); - b.full = dfixed_mul(b, c); - - lb_fill_bw = min(tmp, dfixed_trunc(b)); + lb_fill_bw = min(tmp, wm->disp_clk * wm->bytes_per_pixel / 1000); a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel); b.full = dfixed_const(1000); @@ -1238,14 +1225,14 @@ static void dce_v11_0_program_watermarks(struct amdgpu_device *adev, { struct drm_display_mode *mode = &amdgpu_crtc->base.mode; struct dce10_wm_params wm_low, wm_high; - u32 pixel_period; + u32 active_time; u32 line_time = 0; u32 latency_watermark_a = 0, latency_watermark_b = 0; u32 tmp, wm_mask, lb_vblank_lead_lines = 0; if (amdgpu_crtc->base.enabled && num_heads && mode) { - pixel_period = 1000000 / (u32)mode->clock; - line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535); + active_time = 1000000UL * (u32)mode->crtc_hdisplay / (u32)mode->clock; + line_time = min((u32) (1000000UL * (u32)mode->crtc_htotal / (u32)mode->clock), (u32)65535); /* watermark for high clocks */ if (adev->pm.dpm_enabled) { @@ -1260,7 +1247,7 @@ static void dce_v11_0_program_watermarks(struct amdgpu_device *adev, wm_high.disp_clk = mode->clock; wm_high.src_width = mode->crtc_hdisplay; - wm_high.active_time = mode->crtc_hdisplay * pixel_period; + wm_high.active_time = active_time; wm_high.blank_time = line_time - wm_high.active_time; wm_high.interlaced = false; if (mode->flags & DRM_MODE_FLAG_INTERLACE) @@ -1299,7 +1286,7 @@ static void dce_v11_0_program_watermarks(struct amdgpu_device *adev, wm_low.disp_clk = mode->clock; wm_low.src_width = mode->crtc_hdisplay; - wm_low.active_time = mode->crtc_hdisplay * pixel_period; + wm_low.active_time = active_time; wm_low.blank_time = line_time - wm_low.active_time; wm_low.interlaced = false; if (mode->flags & DRM_MODE_FLAG_INTERLACE) diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c index 9b4dcf76ce6c..d6e51d4b04f0 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c @@ -1096,23 +1096,10 @@ static u32 dce_v8_0_latency_watermark(struct dce8_wm_params *wm) a.full = dfixed_const(available_bandwidth); b.full = dfixed_const(wm->num_heads); a.full = dfixed_div(a, b); + tmp = div_u64((u64) dmif_size * (u64) wm->disp_clk, mc_latency + 512); + tmp = min(dfixed_trunc(a), tmp); - b.full = dfixed_const(mc_latency + 512); - c.full = dfixed_const(wm->disp_clk); - b.full = dfixed_div(b, c); - - c.full = dfixed_const(dmif_size); - b.full = dfixed_div(c, b); - - tmp = min(dfixed_trunc(a), dfixed_trunc(b)); - - b.full = dfixed_const(1000); - c.full = dfixed_const(wm->disp_clk); - b.full = dfixed_div(c, b); - c.full = dfixed_const(wm->bytes_per_pixel); - b.full = dfixed_mul(b, c); - - lb_fill_bw = min(tmp, dfixed_trunc(b)); + lb_fill_bw = min(tmp, wm->disp_clk * wm->bytes_per_pixel / 1000); a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel); b.full = dfixed_const(1000); @@ -1220,14 +1207,14 @@ static void dce_v8_0_program_watermarks(struct amdgpu_device *adev, { struct drm_display_mode *mode = &amdgpu_crtc->base.mode; struct dce8_wm_params wm_low, wm_high; - u32 pixel_period; + u32 active_time; u32 line_time = 0; u32 latency_watermark_a = 0, latency_watermark_b = 0; u32 tmp, wm_mask, lb_vblank_lead_lines = 0; if (amdgpu_crtc->base.enabled && num_heads && mode) { - pixel_period = 1000000 / (u32)mode->clock; - line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535); + active_time = 1000000UL * (u32)mode->crtc_hdisplay / (u32)mode->clock; + line_time = min((u32) (1000000UL * (u32)mode->crtc_htotal / (u32)mode->clock), (u32)65535); /* watermark for high clocks */ if (adev->pm.dpm_enabled) { @@ -1242,7 +1229,7 @@ static void dce_v8_0_program_watermarks(struct amdgpu_device *adev, wm_high.disp_clk = mode->clock; wm_high.src_width = mode->crtc_hdisplay; - wm_high.active_time = mode->crtc_hdisplay * pixel_period; + wm_high.active_time = active_time; wm_high.blank_time = line_time - wm_high.active_time; wm_high.interlaced = false; if (mode->flags & DRM_MODE_FLAG_INTERLACE) @@ -1281,7 +1268,7 @@ static void dce_v8_0_program_watermarks(struct amdgpu_device *adev, wm_low.disp_clk = mode->clock; wm_low.src_width = mode->crtc_hdisplay; - wm_low.active_time = mode->crtc_hdisplay * pixel_period; + wm_low.active_time = active_time; wm_low.blank_time = line_time - wm_low.active_time; wm_low.interlaced = false; if (mode->flags & DRM_MODE_FLAG_INTERLACE) diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 05f6522c0457..b92139e9b9d8 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -113,6 +113,7 @@ struct ast_private { struct ttm_bo_kmap_obj cache_kmap; int next_cursor; bool support_wide_screen; + bool DisableP2A; enum ast_tx_chip tx_chip_type; u8 dp501_maxclk; diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index 9b8f0b975ca6..6c021165ca67 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -124,6 +124,12 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) } else *need_post = false; + /* Check P2A Access */ + ast->DisableP2A = true; + data = ast_read32(ast, 0xf004); + if (data != 0xFFFFFFFF) + ast->DisableP2A = false; + /* Check if we support wide screen */ switch (ast->chip) { case AST1180: @@ -140,15 +146,17 @@ static int ast_detect_chip(struct drm_device *dev, bool *need_post) ast->support_wide_screen = true; else { ast->support_wide_screen = false; - /* Read SCU7c (silicon revision register) */ - ast_write32(ast, 0xf004, 0x1e6e0000); - ast_write32(ast, 0xf000, 0x1); - data = ast_read32(ast, 0x1207c); - data &= 0x300; - if (ast->chip == AST2300 && data == 0x0) /* ast1300 */ - ast->support_wide_screen = true; - if (ast->chip == AST2400 && data == 0x100) /* ast1400 */ - ast->support_wide_screen = true; + if (ast->DisableP2A == false) { + /* Read SCU7c (silicon revision register) */ + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + data = ast_read32(ast, 0x1207c); + data &= 0x300; + if (ast->chip == AST2300 && data == 0x0) /* ast1300 */ + ast->support_wide_screen = true; + if (ast->chip == AST2400 && data == 0x100) /* ast1400 */ + ast->support_wide_screen = true; + } } break; } @@ -216,80 +224,81 @@ static int ast_get_dram_info(struct drm_device *dev) uint32_t data, data2; uint32_t denum, num, div, ref_pll; - ast_write32(ast, 0xf004, 0x1e6e0000); - ast_write32(ast, 0xf000, 0x1); - - - ast_write32(ast, 0x10000, 0xfc600309); - - do { - if (pci_channel_offline(dev->pdev)) - return -EIO; - } while (ast_read32(ast, 0x10000) != 0x01); - data = ast_read32(ast, 0x10004); - - if (data & 0x40) + if (ast->DisableP2A) + { ast->dram_bus_width = 16; + ast->dram_type = AST_DRAM_1Gx16; + ast->mclk = 396; + } else - ast->dram_bus_width = 32; + { + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + data = ast_read32(ast, 0x10004); + + if (data & 0x40) + ast->dram_bus_width = 16; + else + ast->dram_bus_width = 32; + + if (ast->chip == AST2300 || ast->chip == AST2400) { + switch (data & 0x03) { + case 0: + ast->dram_type = AST_DRAM_512Mx16; + break; + default: + case 1: + ast->dram_type = AST_DRAM_1Gx16; + break; + case 2: + ast->dram_type = AST_DRAM_2Gx16; + break; + case 3: + ast->dram_type = AST_DRAM_4Gx16; + break; + } + } else { + switch (data & 0x0c) { + case 0: + case 4: + ast->dram_type = AST_DRAM_512Mx16; + break; + case 8: + if (data & 0x40) + ast->dram_type = AST_DRAM_1Gx16; + else + ast->dram_type = AST_DRAM_512Mx32; + break; + case 0xc: + ast->dram_type = AST_DRAM_1Gx32; + break; + } + } - if (ast->chip == AST2300 || ast->chip == AST2400) { - switch (data & 0x03) { - case 0: - ast->dram_type = AST_DRAM_512Mx16; - break; - default: - case 1: - ast->dram_type = AST_DRAM_1Gx16; - break; - case 2: - ast->dram_type = AST_DRAM_2Gx16; - break; + data = ast_read32(ast, 0x10120); + data2 = ast_read32(ast, 0x10170); + if (data2 & 0x2000) + ref_pll = 14318; + else + ref_pll = 12000; + + denum = data & 0x1f; + num = (data & 0x3fe0) >> 5; + data = (data & 0xc000) >> 14; + switch (data) { case 3: - ast->dram_type = AST_DRAM_4Gx16; - break; - } - } else { - switch (data & 0x0c) { - case 0: - case 4: - ast->dram_type = AST_DRAM_512Mx16; + div = 0x4; break; - case 8: - if (data & 0x40) - ast->dram_type = AST_DRAM_1Gx16; - else - ast->dram_type = AST_DRAM_512Mx32; + case 2: + case 1: + div = 0x2; break; - case 0xc: - ast->dram_type = AST_DRAM_1Gx32; + default: + div = 0x1; break; } + ast->mclk = ref_pll * (num + 2) / (denum + 2) * (div * 1000); } - - data = ast_read32(ast, 0x10120); - data2 = ast_read32(ast, 0x10170); - if (data2 & 0x2000) - ref_pll = 14318; - else - ref_pll = 12000; - - denum = data & 0x1f; - num = (data & 0x3fe0) >> 5; - data = (data & 0xc000) >> 14; - switch (data) { - case 3: - div = 0x4; - break; - case 2: - case 1: - div = 0x2; - break; - default: - div = 0x1; - break; - } - ast->mclk = ref_pll * (num + 2) / (denum + 2) * (div * 1000); return 0; } diff --git a/drivers/gpu/drm/ast/ast_post.c b/drivers/gpu/drm/ast/ast_post.c index 30672a3df8a9..270e8fb2803f 100644 --- a/drivers/gpu/drm/ast/ast_post.c +++ b/drivers/gpu/drm/ast/ast_post.c @@ -375,12 +375,20 @@ void ast_post_gpu(struct drm_device *dev) ast_enable_mmio(dev); ast_set_def_ext_reg(dev); - if (ast->chip == AST2300 || ast->chip == AST2400) - ast_init_dram_2300(dev); - else - ast_init_dram_reg(dev); + if (ast->DisableP2A == false) + { + if (ast->chip == AST2300 || ast->chip == AST2400) + ast_init_dram_2300(dev); + else + ast_init_dram_reg(dev); - ast_init_3rdtx(dev); + ast_init_3rdtx(dev); + } + else + { + if (ast->tx_chip_type != AST_TX_NONE) + ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xa3, 0xcf, 0x80); /* Enable DVO */ + } } /* AST 2300 DRAM settings */ diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 02a60a1df50d..39b8e171cad5 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -75,6 +75,8 @@ #define EDID_QUIRK_FORCE_12BPC (1 << 9) /* Force 6bpc */ #define EDID_QUIRK_FORCE_6BPC (1 << 10) +/* Force 10bpc */ +#define EDID_QUIRK_FORCE_10BPC (1 << 11) struct detailed_mode_closure { struct drm_connector *connector; @@ -125,6 +127,9 @@ static struct edid_quirk { { "FCM", 13600, EDID_QUIRK_PREFER_LARGE_75 | EDID_QUIRK_DETAILED_IN_CM }, + /* LGD panel of HP zBook 17 G2, eDP 10 bpc, but reports unknown bpc */ + { "LGD", 764, EDID_QUIRK_FORCE_10BPC }, + /* LG Philips LCD LP154W01-A5 */ { "LPL", 0, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, { "LPL", 0x2a00, EDID_QUIRK_DETAILED_USE_MAXIMUM_SIZE }, @@ -4478,6 +4483,9 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) if (quirks & EDID_QUIRK_FORCE_8BPC) connector->display_info.bpc = 8; + if (quirks & EDID_QUIRK_FORCE_10BPC) + connector->display_info.bpc = 10; + if (quirks & EDID_QUIRK_FORCE_12BPC) connector->display_info.bpc = 12; diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 04de6fd88f8c..6e4dd62d4ed9 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -46,6 +46,8 @@ #include <linux/slab.h> #include <linux/seq_file.h> #include <linux/export.h> +#include <linux/interval_tree_generic.h> +#include <linux/rbtree.h> /** * DOC: Overview @@ -73,7 +75,8 @@ * allocations and avoiding too much fragmentation. This means free space * searches are O(num_holes). Given that all the fancy features drm_mm supports * something better would be fairly complex and since gfx thrashing is a fairly - * steep cliff not a real concern. Removing a node again is O(1). + * steep cliff not a real concern. Removing a node again is O(1). With the + * rbtree to track free holes, free hole search becomes O(log(num_holes)). * * drm_mm supports a few features: Alignment and range restrictions can be * supplied. Further more every &drm_mm_node has a color value (which is just an @@ -103,6 +106,98 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ u64 end, enum drm_mm_search_flags flags); +#define START(node) ((node)->start) +#define LAST(node) ((node)->start + (node)->size - 1) + +INTERVAL_TREE_DEFINE(struct drm_mm_node, rb, + u64, __subtree_last, + START, LAST, static inline, drm_mm_interval_tree) + +struct drm_mm_node * +drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last) +{ + return drm_mm_interval_tree_iter_first(&mm->interval_tree, + start, last); +} +EXPORT_SYMBOL(drm_mm_interval_first); + +struct drm_mm_node * +drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last) +{ + return drm_mm_interval_tree_iter_next(node, start, last); +} +EXPORT_SYMBOL(drm_mm_interval_next); + +static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node, + struct drm_mm_node *node) +{ + struct drm_mm *mm = hole_node->mm; + struct rb_node **link, *rb; + struct drm_mm_node *parent; + + node->__subtree_last = LAST(node); + + if (hole_node->allocated) { + rb = &hole_node->rb; + while (rb) { + parent = rb_entry(rb, struct drm_mm_node, rb); + if (parent->__subtree_last >= node->__subtree_last) + break; + + parent->__subtree_last = node->__subtree_last; + rb = rb_parent(rb); + } + + rb = &hole_node->rb; + link = &hole_node->rb.rb_right; + } else { + rb = NULL; + link = &mm->interval_tree.rb_node; + } + + while (*link) { + rb = *link; + parent = rb_entry(rb, struct drm_mm_node, rb); + if (parent->__subtree_last < node->__subtree_last) + parent->__subtree_last = node->__subtree_last; + if (node->start < parent->start) + link = &parent->rb.rb_left; + else + link = &parent->rb.rb_right; + } + + rb_link_node(&node->rb, rb, link); + rb_insert_augmented(&node->rb, + &mm->interval_tree, + &drm_mm_interval_tree_augment); +} + +static void +rb_insert_hole_node(struct drm_mm_node *hole_node, struct drm_mm *mm) +{ + struct rb_node **new = &(mm->holes_tree.rb_node); + struct rb_node *parent = NULL; + struct drm_mm_node *cur; + + while (*new) { + parent = *new; + cur = rb_entry(parent, struct drm_mm_node, hole_node); + + if (__drm_mm_hole_node_start(hole_node) + < __drm_mm_hole_node_start(cur)) + new = &parent->rb_left; + else + new = &parent->rb_right; + } + rb_link_node(&hole_node->hole_node, parent, new); + rb_insert_color(&hole_node->hole_node, &mm->holes_tree); +} + +static void rb_erase_hole_node(struct drm_mm_node *hole_node, struct drm_mm *mm) +{ + rb_erase(&hole_node->hole_node, &mm->holes_tree); +} + static void drm_mm_insert_helper(struct drm_mm_node *hole_node, struct drm_mm_node *node, u64 size, unsigned alignment, @@ -142,6 +237,7 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, if (adj_start == hole_start) { hole_node->hole_follows = 0; list_del(&hole_node->hole_stack); + rb_erase_hole_node(hole_node, mm); } node->start = adj_start; @@ -150,14 +246,16 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, node->color = color; node->allocated = 1; - INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list); + drm_mm_interval_tree_add_node(hole_node, node); + BUG_ON(node->start + node->size > adj_end); node->hole_follows = 0; if (__drm_mm_hole_node_start(node) < hole_end) { list_add(&node->hole_stack, &mm->hole_stack); + rb_insert_hole_node(node, mm); node->hole_follows = 1; } } @@ -178,39 +276,54 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, */ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) { - struct drm_mm_node *hole; u64 end = node->start + node->size; - u64 hole_start; - u64 hole_end; + struct drm_mm_node *hole; + u64 hole_start, hole_end; - BUG_ON(node == NULL); + if (WARN_ON(node->size == 0)) + return -EINVAL; /* Find the relevant hole to add our node to */ - drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { - if (hole_start > node->start || hole_end < end) - continue; + hole = drm_mm_interval_tree_iter_first(&mm->interval_tree, + node->start, ~(u64)0); + if (hole) { + if (hole->start < end) + return -ENOSPC; + } else { + hole = list_entry(&mm->head_node.node_list, + typeof(*hole), node_list); + } - node->mm = mm; - node->allocated = 1; + hole = list_last_entry(&hole->node_list, typeof(*hole), node_list); + if (!hole->hole_follows) + return -ENOSPC; - INIT_LIST_HEAD(&node->hole_stack); - list_add(&node->node_list, &hole->node_list); + hole_start = __drm_mm_hole_node_start(hole); + hole_end = __drm_mm_hole_node_end(hole); + if (hole_start > node->start || hole_end < end) + return -ENOSPC; - if (node->start == hole_start) { - hole->hole_follows = 0; - list_del_init(&hole->hole_stack); - } + node->mm = mm; + node->allocated = 1; - node->hole_follows = 0; - if (end != hole_end) { - list_add(&node->hole_stack, &mm->hole_stack); - node->hole_follows = 1; - } + list_add(&node->node_list, &hole->node_list); - return 0; + drm_mm_interval_tree_add_node(hole, node); + + if (node->start == hole_start) { + hole->hole_follows = 0; + list_del(&hole->hole_stack); + rb_erase_hole_node(hole, mm); } - return -ENOSPC; + node->hole_follows = 0; + if (end != hole_end) { + list_add(&node->hole_stack, &mm->hole_stack); + rb_insert_hole_node(node, mm); + node->hole_follows = 1; + } + + return 0; } EXPORT_SYMBOL(drm_mm_reserve_node); @@ -237,6 +350,9 @@ int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, { struct drm_mm_node *hole_node; + if (WARN_ON(size == 0)) + return -EINVAL; + hole_node = drm_mm_search_free_generic(mm, size, alignment, color, sflags); if (!hole_node) @@ -289,6 +405,7 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, if (adj_start == hole_start) { hole_node->hole_follows = 0; list_del(&hole_node->hole_stack); + rb_erase_hole_node(hole_node, mm); } node->start = adj_start; @@ -297,9 +414,10 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, node->color = color; node->allocated = 1; - INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list); + drm_mm_interval_tree_add_node(hole_node, node); + BUG_ON(node->start < start); BUG_ON(node->start < adj_start); BUG_ON(node->start + node->size > adj_end); @@ -308,6 +426,7 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, node->hole_follows = 0; if (__drm_mm_hole_node_start(node) < hole_end) { list_add(&node->hole_stack, &mm->hole_stack); + rb_insert_hole_node(node, mm); node->hole_follows = 1; } } @@ -338,6 +457,9 @@ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *n { struct drm_mm_node *hole_node; + if (WARN_ON(size == 0)) + return -EINVAL; + hole_node = drm_mm_search_free_in_range_generic(mm, size, alignment, color, start, end, sflags); @@ -377,6 +499,7 @@ void drm_mm_remove_node(struct drm_mm_node *node) BUG_ON(__drm_mm_hole_node_start(node) == __drm_mm_hole_node_end(node)); list_del(&node->hole_stack); + rb_erase_hole_node(node, mm); } else BUG_ON(__drm_mm_hole_node_start(node) != __drm_mm_hole_node_end(node)); @@ -385,9 +508,11 @@ void drm_mm_remove_node(struct drm_mm_node *node) if (!prev_node->hole_follows) { prev_node->hole_follows = 1; list_add(&prev_node->hole_stack, &mm->hole_stack); + rb_insert_hole_node(prev_node, mm); } else list_move(&prev_node->hole_stack, &mm->hole_stack); + drm_mm_interval_tree_remove(node, &mm->interval_tree); list_del(&node->node_list); node->allocated = 0; } @@ -410,6 +535,46 @@ static int check_free_hole(u64 start, u64 end, u64 size, unsigned alignment) return end >= start + size; } +static struct drm_mm_node *get_first_hole(const struct drm_mm *mm, + enum drm_mm_search_flags flags) +{ + if (flags & DRM_MM_SEARCH_BOTTOM_UP) { + struct rb_node *node = rb_first(&mm->holes_tree); + + return rb_entry(node, struct drm_mm_node, hole_node); + } else if (flags & DRM_MM_SEARCH_BELOW) { + return list_entry((mm)->hole_stack.prev, + struct drm_mm_node, hole_stack); + } else { + return list_entry((mm)->hole_stack.next, + struct drm_mm_node, hole_stack); + } +} + +static struct drm_mm_node *get_next_hole(struct drm_mm_node *entry, + enum drm_mm_search_flags flags) +{ + if (flags & DRM_MM_SEARCH_BOTTOM_UP) { + return rb_entry(rb_next(&entry->hole_node), + struct drm_mm_node, hole_node); + } else if (flags & DRM_MM_SEARCH_BELOW) { + return list_entry(entry->hole_stack.prev, + struct drm_mm_node, hole_stack); + } else { + return list_entry(entry->hole_stack.next, + struct drm_mm_node, hole_stack); + } +} + +static bool drm_mm_hole_traversal_condition(const struct drm_mm *mm, + struct drm_mm_node *entry, enum drm_mm_search_flags flags) +{ + if (flags & DRM_MM_SEARCH_BOTTOM_UP) + return entry ? 1 : 0; + else + return (&entry->hole_stack != &(mm)->hole_stack) ? 1 : 0; +} + static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, u64 size, unsigned alignment, @@ -427,9 +592,14 @@ static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, best = NULL; best_size = ~0UL; - __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, - flags & DRM_MM_SEARCH_BELOW) { - u64 hole_size = adj_end - adj_start; + for (entry = get_first_hole(mm, flags); + drm_mm_hole_traversal_condition(mm, entry, flags); + entry = get_next_hole(entry, flags)) { + u64 hole_size; + + adj_start = drm_mm_hole_node_start(entry); + adj_end = drm_mm_hole_node_end(entry); + hole_size = adj_end - adj_start; if (mm->color_adjust) { mm->color_adjust(entry, color, &adj_start, &adj_end); @@ -471,9 +641,14 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ best = NULL; best_size = ~0UL; - __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, - flags & DRM_MM_SEARCH_BELOW) { - u64 hole_size = adj_end - adj_start; + for (entry = get_first_hole(mm, flags); + drm_mm_hole_traversal_condition(mm, entry, flags); + entry = get_next_hole(entry, flags)) { + u64 hole_size; + + adj_start = drm_mm_hole_node_start(entry); + adj_end = drm_mm_hole_node_end(entry); + hole_size = adj_end - adj_start; if (adj_start < start) adj_start = start; @@ -514,14 +689,21 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) { list_replace(&old->node_list, &new->node_list); list_replace(&old->hole_stack, &new->hole_stack); + rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree); new->hole_follows = old->hole_follows; new->mm = old->mm; new->start = old->start; new->size = old->size; new->color = old->color; + new->__subtree_last = old->__subtree_last; old->allocated = 0; new->allocated = 1; + + if (old->hole_follows) + rb_replace_node(&old->hole_node, &new->hole_node, + &old->mm->holes_tree); + } EXPORT_SYMBOL(drm_mm_replace_node); @@ -746,7 +928,6 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size) /* Clever trick to avoid a special case in the free hole tracking. */ INIT_LIST_HEAD(&mm->head_node.node_list); - INIT_LIST_HEAD(&mm->head_node.hole_stack); mm->head_node.hole_follows = 1; mm->head_node.scanned_block = 0; mm->head_node.scanned_prev_free = 0; @@ -756,7 +937,10 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size) mm->head_node.size = start - mm->head_node.start; list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack); + mm->interval_tree = RB_ROOT; mm->color_adjust = NULL; + mm->holes_tree = RB_ROOT; + rb_insert_hole_node(&mm->head_node, mm); } EXPORT_SYMBOL(drm_mm_init); diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index ce0645d0c1e5..61e3a097a478 100644 --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c @@ -783,20 +783,23 @@ void psb_intel_lvds_init(struct drm_device *dev, if (scan->type & DRM_MODE_TYPE_PREFERRED) { mode_dev->panel_fixed_mode = drm_mode_duplicate(dev, scan); + DRM_DEBUG_KMS("Using mode from DDC\n"); goto out; /* FIXME: check for quirks */ } } /* Failed to get EDID, what about VBT? do we need this? */ - if (mode_dev->vbt_mode) + if (dev_priv->lfp_lvds_vbt_mode) { mode_dev->panel_fixed_mode = - drm_mode_duplicate(dev, mode_dev->vbt_mode); + drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); - if (!mode_dev->panel_fixed_mode) - if (dev_priv->lfp_lvds_vbt_mode) - mode_dev->panel_fixed_mode = - drm_mode_duplicate(dev, - dev_priv->lfp_lvds_vbt_mode); + if (mode_dev->panel_fixed_mode) { + mode_dev->panel_fixed_mode->type |= + DRM_MODE_TYPE_PREFERRED; + DRM_DEBUG_KMS("Using mode from VBT\n"); + goto out; + } + } /* * If we didn't get EDID, try checking if the panel is already turned @@ -813,6 +816,7 @@ void psb_intel_lvds_init(struct drm_device *dev, if (mode_dev->panel_fixed_mode) { mode_dev->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; + DRM_DEBUG_KMS("Using pre-programmed mode\n"); goto out; /* FIXME: check for quirks */ } } diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 5838545468f8..dbc198b00792 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -12,6 +12,8 @@ config DRM_MSM select QCOM_SCM select BACKLIGHT_CLASS_DEVICE select MSM_EXT_DISPLAY + select MMU_NOTIFIER + select INTERVAL_TREE default y help DRM/KMS driver for MSM/snapdragon. diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index f3a8a8416c7a..84125b3d1f95 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -13,6 +13,7 @@ msm_drm-y := \ hdmi/hdmi_connector.o \ hdmi/hdmi_hdcp.o \ hdmi/hdmi_i2c.o \ + hdmi/hdmi_util.o \ hdmi/hdmi_phy_8960.o \ hdmi/hdmi_phy_8x60.o \ hdmi/hdmi_phy_8x74.o \ @@ -50,7 +51,8 @@ msm_drm-y := \ sde_dbg_evtlog.o \ sde_io_util.o \ dba_bridge.o \ - sde_edid_parser.o + sde_edid_parser.o \ + sde_hdcp_1x.o # use drm gpu driver only if qcom_kgsl driver not available ifneq ($(CONFIG_QCOM_KGSL),y) @@ -101,9 +103,11 @@ msm_drm-$(CONFIG_DRM_MSM_DSI_STAGING) += dsi-staging/dsi_phy.o \ dsi-staging/dsi_display_test.o msm_drm-$(CONFIG_DRM_SDE_HDMI) += \ + hdmi-staging/sde_hdmi_util.o \ hdmi-staging/sde_hdmi.o \ hdmi-staging/sde_hdmi_bridge.o \ hdmi-staging/sde_hdmi_audio.o \ + hdmi-staging/sde_hdmi_hdcp2p2.o \ msm_drm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \ dsi/pll/dsi_pll_28nm.o @@ -144,6 +148,8 @@ msm_drm-$(CONFIG_DRM_MSM) += \ msm_rd.o \ msm_ringbuffer.o \ msm_prop.o \ - msm_snapshot.o + msm_snapshot.o \ + msm_submitqueue.o \ + msm_trace_points.o obj-$(CONFIG_DRM_MSM) += msm_drm.o diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 3caa460aa5ba..e27ea604ac5b 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -13,6 +13,7 @@ #include "msm_gem.h" #include "msm_iommu.h" +#include "msm_trace.h" #include "a5xx_gpu.h" #define SECURE_VA_START 0xc0000000 @@ -46,7 +47,6 @@ static void a5xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring) static void a5xx_set_pagetable(struct msm_gpu *gpu, struct msm_ringbuffer *ring, struct msm_gem_address_space *aspace) { - struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct msm_mmu *mmu = aspace->mmu; struct msm_iommu *iommu = to_msm_iommu(mmu); @@ -75,17 +75,15 @@ static void a5xx_set_pagetable(struct msm_gpu *gpu, struct msm_ringbuffer *ring, * reload the pagetable if the current ring gets preempted out. */ OUT_PKT7(ring, CP_MEM_WRITE, 4); - OUT_RING(ring, lower_32_bits(rbmemptr(adreno_gpu, ring->id, ttbr0))); - OUT_RING(ring, upper_32_bits(rbmemptr(adreno_gpu, ring->id, ttbr0))); + OUT_RING(ring, lower_32_bits(rbmemptr(ring, ttbr0))); + OUT_RING(ring, upper_32_bits(rbmemptr(ring, ttbr0))); OUT_RING(ring, lower_32_bits(iommu->ttbr0)); OUT_RING(ring, upper_32_bits(iommu->ttbr0)); /* Also write the current contextidr (ASID) */ OUT_PKT7(ring, CP_MEM_WRITE, 3); - OUT_RING(ring, lower_32_bits(rbmemptr(adreno_gpu, ring->id, - contextidr))); - OUT_RING(ring, upper_32_bits(rbmemptr(adreno_gpu, ring->id, - contextidr))); + OUT_RING(ring, lower_32_bits(rbmemptr(ring, contextidr))); + OUT_RING(ring, upper_32_bits(rbmemptr(ring, contextidr))); OUT_RING(ring, iommu->contextidr); /* Invalidate the draw state so we start off fresh */ @@ -103,12 +101,32 @@ static void a5xx_set_pagetable(struct msm_gpu *gpu, struct msm_ringbuffer *ring, OUT_RING(ring, 1); } +/* Inline PM4 code to get the current value of the 19.2 Mhz always on counter */ +static void a5xx_get_ticks(struct msm_ringbuffer *ring, uint64_t iova) +{ + /* + * Set bit[30] to make this command a 64 bit write operation. + * bits[18-29] is to specify number of consecutive registers + * to copy, so set this space with 2, since we want to copy + * data from REG_A5XX_RBBM_ALWAYSON_COUNTER_LO and [HI]. + */ + + OUT_PKT7(ring, CP_REG_TO_MEM, 3); + OUT_RING(ring, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO | + (1 << 30) | (2 << 18)); + OUT_RING(ring, lower_32_bits(iova)); + OUT_RING(ring, upper_32_bits(iova)); +} + static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); struct msm_ringbuffer *ring = gpu->rb[submit->ring]; unsigned int i, ibs = 0; + unsigned long flags; + u64 ticks; + ktime_t time; a5xx_set_pagetable(gpu, ring, submit->aspace); @@ -142,24 +160,15 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) OUT_RING(ring, 1); } - /* Record the always on counter before command execution */ - if (submit->profile_buf_iova) { - uint64_t gpuaddr = submit->profile_buf_iova + - offsetof(struct drm_msm_gem_submit_profile_buffer, - ticks_submitted); + /* Record the GPU ticks at command start for kernel side profiling */ + a5xx_get_ticks(ring, + RING_TICKS_IOVA(ring, submit->tick_index, started)); - /* - * Set bit[30] to make this command a 64 bit write operation. - * bits[18-29] is to specify number of consecutive registers - * to copy, so set this space with 2, since we want to copy - * data from REG_A5XX_RBBM_ALWAYSON_COUNTER_LO and [HI]. - */ - OUT_PKT7(ring, CP_REG_TO_MEM, 3); - OUT_RING(ring, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO | - (1 << 30) | (2 << 18)); - OUT_RING(ring, lower_32_bits(gpuaddr)); - OUT_RING(ring, upper_32_bits(gpuaddr)); - } + /* And for the user profiling too if it is enabled */ + if (submit->profile_buf_iova) + a5xx_get_ticks(ring, submit->profile_buf_iova + + offsetof(struct drm_msm_gem_submit_profile_buffer, + ticks_submitted)); /* Submit the commands */ for (i = 0; i < submit->nr_cmds; i++) { @@ -193,18 +202,15 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) OUT_PKT7(ring, CP_YIELD_ENABLE, 1); OUT_RING(ring, 0x01); + /* Record the GPU ticks at command retire for kernel side profiling */ + a5xx_get_ticks(ring, + RING_TICKS_IOVA(ring, submit->tick_index, retired)); + /* Record the always on counter after command execution */ - if (submit->profile_buf_iova) { - uint64_t gpuaddr = submit->profile_buf_iova + + if (submit->profile_buf_iova) + a5xx_get_ticks(ring, submit->profile_buf_iova + offsetof(struct drm_msm_gem_submit_profile_buffer, - ticks_retired); - - OUT_PKT7(ring, CP_REG_TO_MEM, 3); - OUT_RING(ring, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO | - (1 << 30) | (2 << 18)); - OUT_RING(ring, lower_32_bits(gpuaddr)); - OUT_RING(ring, upper_32_bits(gpuaddr)); - } + ticks_retired)); /* Write the fence to the scratch register */ OUT_PKT4(ring, REG_A5XX_CP_SCRATCH_REG(2), 1); @@ -217,8 +223,8 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) OUT_PKT7(ring, CP_EVENT_WRITE, 4); OUT_RING(ring, CACHE_FLUSH_TS | (1 << 31)); - OUT_RING(ring, lower_32_bits(rbmemptr(adreno_gpu, ring->id, fence))); - OUT_RING(ring, upper_32_bits(rbmemptr(adreno_gpu, ring->id, fence))); + OUT_RING(ring, lower_32_bits(rbmemptr(ring, fence))); + OUT_RING(ring, upper_32_bits(rbmemptr(ring, fence))); OUT_RING(ring, submit->fence); if (submit->secure) { @@ -240,33 +246,28 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) /* Set bit 0 to trigger an interrupt on preempt complete */ OUT_RING(ring, 0x01); - if (submit->profile_buf_iova) { - unsigned long flags; - uint64_t ktime; - struct drm_msm_gem_submit_profile_buffer *profile_buf = - submit->profile_buf_vaddr; - - /* - * With this profiling, we are trying to create closest - * possible mapping between the CPU time domain(monotonic clock) - * and the GPU time domain(ticks). In order to make this - * happen, we need to briefly turn off interrupts to make sure - * interrupts do not run between collecting these two samples. - */ - local_irq_save(flags); - - profile_buf->ticks_queued = gpu_read64(gpu, - REG_A5XX_RBBM_ALWAYSON_COUNTER_LO, - REG_A5XX_RBBM_ALWAYSON_COUNTER_HI); + /* + * Get the current kernel time and ticks with interrupts off so we don't + * get interrupted between the operations and skew the numbers + */ - ktime = ktime_get_raw_ns(); + local_irq_save(flags); + ticks = gpu_read64(gpu, REG_A5XX_RBBM_ALWAYSON_COUNTER_LO, + REG_A5XX_RBBM_ALWAYSON_COUNTER_HI); + time = ktime_get_raw(); + local_irq_restore(flags); - local_irq_restore(flags); + if (submit->profile_buf) { + struct timespec64 ts = ktime_to_timespec64(time); - profile_buf->queue_time = ktime; - profile_buf->submit_time = ktime; + /* Write the data into the user-specified profile buffer */ + submit->profile_buf->time.tv_sec = ts.tv_sec; + submit->profile_buf->time.tv_nsec = ts.tv_nsec; + submit->profile_buf->ticks_queued = ticks; } + trace_msm_submitted(submit, ticks, ktime_to_ns(time)); + a5xx_flush(gpu, ring); /* Check to see if we need to start preemption */ @@ -477,32 +478,14 @@ static int a5xx_preempt_start(struct msm_gpu *gpu) static struct drm_gem_object *a5xx_ucode_load_bo(struct msm_gpu *gpu, const struct firmware *fw, u64 *iova) { - struct drm_device *drm = gpu->dev; struct drm_gem_object *bo; void *ptr; - mutex_lock(&drm->struct_mutex); - bo = msm_gem_new(drm, fw->size - 4, - MSM_BO_UNCACHED | MSM_BO_GPU_READONLY); - mutex_unlock(&drm->struct_mutex); + ptr = msm_gem_kernel_new(gpu->dev, fw->size - 4, + MSM_BO_UNCACHED | MSM_BO_GPU_READONLY, gpu->aspace, &bo, iova); - if (IS_ERR(bo)) - return bo; - - ptr = msm_gem_vaddr(bo); - if (!ptr) { - drm_gem_object_unreference_unlocked(bo); - return ERR_PTR(-ENOMEM); - } - - if (iova) { - int ret = msm_gem_get_iova(bo, gpu->aspace, iova); - - if (ret) { - drm_gem_object_unreference_unlocked(bo); - return ERR_PTR(ret); - } - } + if (IS_ERR(ptr)) + return ERR_CAST(ptr); memcpy(ptr, &fw->data[4], fw->size - 4); return bo; @@ -1408,8 +1391,8 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) * Set the user domain range to fall into the TTBR1 region for global * objects */ - a5xx_config.va_start = 0x800000000; - a5xx_config.va_end = 0x8ffffffff; + a5xx_config.va_start = 0xfffffff000000000ULL; + a5xx_config.va_end = 0xffffffffffffffffULL; a5xx_config.secure_va_start = SECURE_VA_START; a5xx_config.secure_va_end = SECURE_VA_START + SECURE_VA_SIZE - 1; diff --git a/drivers/gpu/drm/msm/adreno/a5xx_power.c b/drivers/gpu/drm/msm/adreno/a5xx_power.c index e04feaadefb9..647b61313fc2 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_power.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_power.c @@ -458,20 +458,10 @@ void a5xx_gpmu_ucode_init(struct msm_gpu *gpu) */ bosize = (cmds_size + (cmds_size / TYPE4_MAX_PAYLOAD) + 1) << 2; - mutex_lock(&drm->struct_mutex); - a5xx_gpu->gpmu_bo = msm_gem_new(drm, bosize, - MSM_BO_UNCACHED | MSM_BO_GPU_READONLY); - mutex_unlock(&drm->struct_mutex); - - if (IS_ERR(a5xx_gpu->gpmu_bo)) - goto err; - - if (msm_gem_get_iova(a5xx_gpu->gpmu_bo, gpu->aspace, - &a5xx_gpu->gpmu_iova)) - goto err; - - ptr = msm_gem_vaddr(a5xx_gpu->gpmu_bo); - if (!ptr) + ptr = msm_gem_kernel_new(drm, bosize, + MSM_BO_UNCACHED | MSM_BO_GPU_READONLY, gpu->aspace, + &a5xx_gpu->gpmu_bo, &a5xx_gpu->gpmu_iova); + if (IS_ERR(ptr)) goto err; while (cmds_size > 0) { diff --git a/drivers/gpu/drm/msm/adreno/a5xx_preempt.c b/drivers/gpu/drm/msm/adreno/a5xx_preempt.c index 648494c75abc..57ef366cf82c 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_preempt.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_preempt.c @@ -15,43 +15,6 @@ #include "msm_iommu.h" #include "a5xx_gpu.h" -static void *alloc_kernel_bo(struct drm_device *drm, struct msm_gpu *gpu, - size_t size, uint32_t flags, struct drm_gem_object **bo, - u64 *iova) -{ - struct drm_gem_object *_bo; - u64 _iova; - void *ptr; - int ret; - - mutex_lock(&drm->struct_mutex); - _bo = msm_gem_new(drm, size, flags); - mutex_unlock(&drm->struct_mutex); - - if (IS_ERR(_bo)) - return _bo; - - ret = msm_gem_get_iova(_bo, gpu->aspace, &_iova); - if (ret) - goto out; - - ptr = msm_gem_vaddr(_bo); - if (!ptr) { - ret = -ENOMEM; - goto out; - } - - if (bo) - *bo = _bo; - if (iova) - *iova = _iova; - - return ptr; -out: - drm_gem_object_unreference_unlocked(_bo); - return ERR_PTR(ret); -} - /* * Try to transition the preemption state from old to new. Return * true on success or false if the original state wasn't 'old' @@ -102,7 +65,6 @@ static inline void update_wptr(struct msm_gpu *gpu, struct msm_ringbuffer *ring) /* Return the highest priority ringbuffer with something in it */ static struct msm_ringbuffer *get_next_ring(struct msm_gpu *gpu) { - struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); unsigned long flags; int i; @@ -111,7 +73,7 @@ static struct msm_ringbuffer *get_next_ring(struct msm_gpu *gpu) struct msm_ringbuffer *ring = gpu->rb[i]; spin_lock_irqsave(&ring->lock, flags); - empty = (get_wptr(ring) == adreno_gpu->memptrs->rptr[ring->id]); + empty = (get_wptr(ring) == ring->memptrs->rptr); spin_unlock_irqrestore(&ring->lock, flags); if (!empty) @@ -178,10 +140,8 @@ void a5xx_preempt_trigger(struct msm_gpu *gpu) /* Set the SMMU info for the preemption */ if (a5xx_gpu->smmu_info) { - a5xx_gpu->smmu_info->ttbr0 = - adreno_gpu->memptrs->ttbr0[ring->id]; - a5xx_gpu->smmu_info->contextidr = - adreno_gpu->memptrs->contextidr[ring->id]; + a5xx_gpu->smmu_info->ttbr0 = ring->memptrs->ttbr0; + a5xx_gpu->smmu_info->contextidr = ring->memptrs->contextidr; } /* Set the address of the incoming preemption record */ @@ -280,10 +240,10 @@ static int preempt_init_ring(struct a5xx_gpu *a5xx_gpu, struct drm_gem_object *bo; u64 iova; - ptr = alloc_kernel_bo(gpu->dev, gpu, + ptr = msm_gem_kernel_new(gpu->dev, A5XX_PREEMPT_RECORD_SIZE + A5XX_PREEMPT_COUNTER_SIZE, MSM_BO_UNCACHED | MSM_BO_PRIVILEGED, - &bo, &iova); + gpu->aspace, &bo, &iova); if (IS_ERR(ptr)) return PTR_ERR(ptr); @@ -298,7 +258,7 @@ static int preempt_init_ring(struct a5xx_gpu *a5xx_gpu, ptr->info = 0; ptr->data = 0; ptr->cntl = MSM_GPU_RB_CNTL_DEFAULT; - ptr->rptr_addr = rbmemptr(adreno_gpu, ring->id, rptr); + ptr->rptr_addr = rbmemptr(ring, rptr); ptr->counter = iova + A5XX_PREEMPT_RECORD_SIZE; return 0; @@ -354,10 +314,10 @@ void a5xx_preempt_init(struct msm_gpu *gpu) } if (msm_iommu_allow_dynamic(gpu->aspace->mmu)) { - ptr = alloc_kernel_bo(gpu->dev, gpu, + ptr = msm_gem_kernel_new(gpu->dev, sizeof(struct a5xx_smmu_info), MSM_BO_UNCACHED | MSM_BO_PRIVILEGED, - &bo, &iova); + gpu->aspace, &bo, &iova); if (IS_ERR(ptr)) goto fail; diff --git a/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c b/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c index 690e6f546e60..d1c1ab460c95 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_snapshot.c @@ -214,27 +214,14 @@ struct crashdump { static int crashdump_init(struct msm_gpu *gpu, struct crashdump *crashdump) { - struct drm_device *drm = gpu->dev; - int ret = -ENOMEM; - - crashdump->bo = msm_gem_new(drm, CRASHDUMP_BO_SIZE, MSM_BO_UNCACHED); - if (IS_ERR(crashdump->bo)) { - ret = PTR_ERR(crashdump->bo); - crashdump->bo = NULL; - return ret; - } - - crashdump->ptr = msm_gem_vaddr_locked(crashdump->bo); - if (!crashdump->ptr) - goto out; - - ret = msm_gem_get_iova_locked(crashdump->bo, gpu->aspace, - &crashdump->iova); - -out: - if (ret) { - drm_gem_object_unreference(crashdump->bo); - crashdump->bo = NULL; + int ret = 0; + + crashdump->ptr = msm_gem_kernel_new_locked(gpu->dev, + CRASHDUMP_BO_SIZE, MSM_BO_UNCACHED, + gpu->aspace, &crashdump->bo, &crashdump->iova); + if (IS_ERR(crashdump->ptr)) { + ret = PTR_ERR(crashdump->ptr); + crashdump->ptr = NULL; } return ret; diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index d3cb497411c4..2a5683e3034b 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -51,6 +51,9 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value) if (adreno_gpu->funcs->get_timestamp) return adreno_gpu->funcs->get_timestamp(gpu, value); return -EINVAL; + case MSM_PARAM_NR_RINGS: + *value = gpu->nr_rings; + return 0; default: DBG("%s: invalid param: %u", gpu->name, param); return -EINVAL; @@ -90,7 +93,7 @@ int adreno_hw_init(struct msm_gpu *gpu) REG_ADRENO_CP_RB_BASE_HI, gpu->rb[0]->iova); adreno_gpu_write64(adreno_gpu, REG_ADRENO_CP_RB_RPTR_ADDR, - REG_ADRENO_CP_RB_RPTR_ADDR_HI, rbmemptr(adreno_gpu, 0, rptr)); + REG_ADRENO_CP_RB_RPTR_ADDR_HI, rbmemptr(gpu->rb[0], rptr)); return 0; } @@ -106,10 +109,11 @@ static uint32_t get_rptr(struct adreno_gpu *adreno_gpu, * ensure that it won't be. If not then this is why your * a430 stopped working. */ - return adreno_gpu->memptrs->rptr[ring->id] = adreno_gpu_read( - adreno_gpu, REG_ADRENO_CP_RB_RPTR); - } else - return adreno_gpu->memptrs->rptr[ring->id]; + return ring->memptrs->rptr = + adreno_gpu_read(adreno_gpu, REG_ADRENO_CP_RB_RPTR); + } + + return ring->memptrs->rptr; } struct msm_ringbuffer *adreno_active_ring(struct msm_gpu *gpu) @@ -128,17 +132,11 @@ uint32_t adreno_submitted_fence(struct msm_gpu *gpu, uint32_t adreno_last_fence(struct msm_gpu *gpu, struct msm_ringbuffer *ring) { - struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); - - if (!ring) - return 0; - - return adreno_gpu->memptrs->fence[ring->id]; + return ring ? ring->memptrs->fence : 0; } void adreno_recover(struct msm_gpu *gpu) { - struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct drm_device *dev = gpu->dev; struct msm_ringbuffer *ring; int ret, i; @@ -156,9 +154,8 @@ void adreno_recover(struct msm_gpu *gpu) ring->next = ring->start; /* reset completed fence seqno, discard anything pending: */ - adreno_gpu->memptrs->fence[ring->id] = - adreno_submitted_fence(gpu, ring); - adreno_gpu->memptrs->rptr[ring->id] = 0; + ring->memptrs->fence = adreno_submitted_fence(gpu, ring); + ring->memptrs->rptr = 0; } gpu->funcs->pm_resume(gpu); @@ -213,7 +210,7 @@ void adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) OUT_PKT3(ring, CP_EVENT_WRITE, 3); OUT_RING(ring, CACHE_FLUSH_TS); - OUT_RING(ring, rbmemptr(adreno_gpu, ring->id, fence)); + OUT_RING(ring, rbmemptr(ring, fence)); OUT_RING(ring, submit->fence); /* we could maybe be clever and only CP_COND_EXEC the interrupt: */ @@ -516,7 +513,6 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, { struct adreno_platform_config *config = pdev->dev.platform_data; struct msm_gpu *gpu = &adreno_gpu->base; - struct msm_mmu *mmu; int ret; adreno_gpu->funcs = funcs; @@ -541,79 +537,19 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, } ret = request_firmware(&adreno_gpu->pfp, adreno_gpu->info->pfpfw, drm->dev); - if (ret) { + if (ret) dev_err(drm->dev, "failed to load %s PFP firmware: %d\n", adreno_gpu->info->pfpfw, ret); - return ret; - } - mmu = gpu->aspace->mmu; - if (mmu) { - ret = mmu->funcs->attach(mmu, NULL, 0); - if (ret) - return ret; - } - - if (gpu->secure_aspace) { - mmu = gpu->secure_aspace->mmu; - if (mmu) { - ret = mmu->funcs->attach(mmu, NULL, 0); - if (ret) - return ret; - } - } - - mutex_lock(&drm->struct_mutex); - adreno_gpu->memptrs_bo = msm_gem_new(drm, sizeof(*adreno_gpu->memptrs), - MSM_BO_UNCACHED); - mutex_unlock(&drm->struct_mutex); - if (IS_ERR(adreno_gpu->memptrs_bo)) { - ret = PTR_ERR(adreno_gpu->memptrs_bo); - adreno_gpu->memptrs_bo = NULL; - dev_err(drm->dev, "could not allocate memptrs: %d\n", ret); - return ret; - } - - adreno_gpu->memptrs = msm_gem_vaddr(adreno_gpu->memptrs_bo); - if (!adreno_gpu->memptrs) { - dev_err(drm->dev, "could not vmap memptrs\n"); - return -ENOMEM; - } - - ret = msm_gem_get_iova(adreno_gpu->memptrs_bo, gpu->aspace, - &adreno_gpu->memptrs_iova); - if (ret) { - dev_err(drm->dev, "could not map memptrs: %d\n", ret); - return ret; - } - - return 0; + return ret; } void adreno_gpu_cleanup(struct adreno_gpu *gpu) { - struct msm_gem_address_space *aspace = gpu->base.aspace; - - if (gpu->memptrs_bo) { - if (gpu->memptrs_iova) - msm_gem_put_iova(gpu->memptrs_bo, aspace); - drm_gem_object_unreference_unlocked(gpu->memptrs_bo); - } release_firmware(gpu->pm4); release_firmware(gpu->pfp); msm_gpu_cleanup(&gpu->base); - - if (aspace) { - aspace->mmu->funcs->detach(aspace->mmu); - msm_gem_address_space_put(aspace); - } - - if (gpu->base.secure_aspace) { - aspace = gpu->base.secure_aspace; - aspace->mmu->funcs->detach(aspace->mmu); - msm_gem_address_space_put(aspace); - } } static void adreno_snapshot_os(struct msm_gpu *gpu, diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 9e622fa06ce4..c894956fb5e8 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -83,22 +83,6 @@ struct adreno_info { const struct adreno_info *adreno_info(struct adreno_rev rev); -#define _sizeof(member) \ - sizeof(((struct adreno_rbmemptrs *) 0)->member[0]) - -#define _base(adreno_gpu, member) \ - ((adreno_gpu)->memptrs_iova + offsetof(struct adreno_rbmemptrs, member)) - -#define rbmemptr(adreno_gpu, index, member) \ - (_base((adreno_gpu), member) + ((index) * _sizeof(member))) - -struct adreno_rbmemptrs { - volatile uint32_t rptr[MSM_GPU_MAX_RINGS]; - volatile uint32_t fence[MSM_GPU_MAX_RINGS]; - volatile uint64_t ttbr0[MSM_GPU_MAX_RINGS]; - volatile unsigned int contextidr[MSM_GPU_MAX_RINGS]; -}; - struct adreno_counter { u32 lo; u32 hi; @@ -137,13 +121,6 @@ struct adreno_gpu { /* firmware: */ const struct firmware *pm4, *pfp; - /* ringbuffer rptr/wptr: */ - // TODO should this be in msm_ringbuffer? I think it would be - // different for z180.. - struct adreno_rbmemptrs *memptrs; - struct drm_gem_object *memptrs_bo; - uint64_t memptrs_iova; - /* * Register offsets are different between some GPUs. * GPU specific offsets will be exported by GPU specific diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c index c34713a13332..309401eb3093 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c @@ -20,6 +20,7 @@ #include "msm_kms.h" #include "sde_connector.h" #include "dsi_drm.h" +#include "sde_trace.h" #define to_dsi_bridge(x) container_of((x), struct dsi_bridge, base) #define to_dsi_state(x) container_of((x), struct dsi_connector_state, base) @@ -138,19 +139,24 @@ static void dsi_bridge_pre_enable(struct drm_bridge *bridge) return; } + SDE_ATRACE_BEGIN("dsi_bridge_pre_enable"); rc = dsi_display_prepare(c_bridge->display); if (rc) { pr_err("[%d] DSI display prepare failed, rc=%d\n", c_bridge->id, rc); + SDE_ATRACE_END("dsi_bridge_pre_enable"); return; } + SDE_ATRACE_BEGIN("dsi_display_enable"); rc = dsi_display_enable(c_bridge->display); if (rc) { pr_err("[%d] DSI display enable failed, rc=%d\n", c_bridge->id, rc); (void)dsi_display_unprepare(c_bridge->display); } + SDE_ATRACE_END("dsi_display_enable"); + SDE_ATRACE_END("dsi_bridge_pre_enable"); } static void dsi_bridge_enable(struct drm_bridge *bridge) @@ -201,19 +207,25 @@ static void dsi_bridge_post_disable(struct drm_bridge *bridge) return; } + SDE_ATRACE_BEGIN("dsi_bridge_post_disable"); + SDE_ATRACE_BEGIN("dsi_display_disable"); rc = dsi_display_disable(c_bridge->display); if (rc) { pr_err("[%d] DSI display disable failed, rc=%d\n", c_bridge->id, rc); + SDE_ATRACE_END("dsi_display_disable"); return; } + SDE_ATRACE_END("dsi_display_disable"); rc = dsi_display_unprepare(c_bridge->display); if (rc) { pr_err("[%d] DSI display unprepare failed, rc=%d\n", c_bridge->id, rc); + SDE_ATRACE_END("dsi_bridge_post_disable"); return; } + SDE_ATRACE_END("dsi_bridge_post_disable"); } static void dsi_bridge_mode_set(struct drm_bridge *bridge, diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 4580a6e3c877..e2b8deda46c2 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -838,22 +838,19 @@ static int dsi_tx_buf_alloc(struct msm_dsi_host *msm_host, int size) int ret; u64 iova; - mutex_lock(&dev->struct_mutex); msm_host->tx_gem_obj = msm_gem_new(dev, size, MSM_BO_UNCACHED); if (IS_ERR(msm_host->tx_gem_obj)) { ret = PTR_ERR(msm_host->tx_gem_obj); pr_err("%s: failed to allocate gem, %d\n", __func__, ret); msm_host->tx_gem_obj = NULL; - mutex_unlock(&dev->struct_mutex); return ret; } - ret = msm_gem_get_iova_locked(msm_host->tx_gem_obj, 0, &iova); + ret = msm_gem_get_iova(msm_host->tx_gem_obj, NULL, &iova); if (ret) { pr_err("%s: failed to get iova, %d\n", __func__, ret); return ret; } - mutex_unlock(&dev->struct_mutex); if (iova & 0x07) { pr_err("%s: buf NOT 8 bytes aligned\n", __func__); diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 4c70472bd338..2ee75a2c1039 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -29,6 +29,8 @@ #include "sde_connector.h" #include "msm_drv.h" #include "sde_hdmi.h" +#include "sde_hdmi_regs.h" +#include "hdmi.h" static DEFINE_MUTEX(sde_hdmi_list_lock); static LIST_HEAD(sde_hdmi_list); @@ -49,6 +51,9 @@ static LIST_HEAD(sde_hdmi_list); #define HDMI_SCDC_ERR_DET_2_H 0x55 #define HDMI_SCDC_ERR_DET_CHECKSUM 0x56 +#define HDMI_DISPLAY_MAX_WIDTH 4096 +#define HDMI_DISPLAY_MAX_HEIGHT 2160 + static const struct of_device_id sde_hdmi_dt_match[] = { {.compatible = "qcom,hdmi-display"}, {} @@ -367,6 +372,135 @@ static ssize_t _sde_hdmi_edid_vendor_name_read(struct file *file, return len; } +static ssize_t _sde_hdmi_src_hdcp14_support_read(struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + struct sde_hdmi *display = file->private_data; + char buf[SZ_128]; + u32 len = 0; + + if (!display) + return -ENODEV; + + if (!display->ctrl.ctrl) { + SDE_ERROR("hdmi is NULL\n"); + return -ENOMEM; + } + + SDE_HDMI_DEBUG("%s +", __func__); + if (*ppos) + return 0; + + if (display->hdcp14_present) + len += snprintf(buf, SZ_128 - len, "true\n"); + else + len += snprintf(buf, SZ_128 - len, "false\n"); + + if (copy_to_user(buff, buf, len)) + return -EFAULT; + + *ppos += len; + SDE_HDMI_DEBUG("%s - ", __func__); + return len; +} + +static ssize_t _sde_hdmi_src_hdcp22_support_read(struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + struct sde_hdmi *display = file->private_data; + char buf[SZ_128]; + u32 len = 0; + + if (!display) + return -ENODEV; + + if (!display->ctrl.ctrl) { + SDE_ERROR("hdmi is NULL\n"); + return -ENOMEM; + } + + SDE_HDMI_DEBUG("%s +", __func__); + if (*ppos) + return 0; + + if (display->src_hdcp22_support) + len += snprintf(buf, SZ_128 - len, "true\n"); + else + len += snprintf(buf, SZ_128 - len, "false\n"); + + if (copy_to_user(buff, buf, len)) + return -EFAULT; + + *ppos += len; + SDE_HDMI_DEBUG("%s - ", __func__); + return len; +} + +static ssize_t _sde_hdmi_sink_hdcp22_support_read(struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + struct sde_hdmi *display = file->private_data; + char buf[SZ_128]; + u32 len = 0; + + if (!display) + return -ENODEV; + + if (!display->ctrl.ctrl) { + SDE_ERROR("hdmi is NULL\n"); + return -ENOMEM; + } + + SDE_HDMI_DEBUG("%s +", __func__); + if (*ppos) + return 0; + + if (display->sink_hdcp22_support) + len += snprintf(buf, SZ_128 - len, "true\n"); + else + len += snprintf(buf, SZ_128 - len, "false\n"); + + if (copy_to_user(buff, buf, len)) + return -EFAULT; + + *ppos += len; + SDE_HDMI_DEBUG("%s - ", __func__); + return len; +} + +static ssize_t _sde_hdmi_hdcp_state_read(struct file *file, + char __user *buff, + size_t count, + loff_t *ppos) +{ + struct sde_hdmi *display = file->private_data; + char buf[SZ_128]; + u32 len = 0; + + if (!display) + return -ENODEV; + + SDE_HDMI_DEBUG("%s +", __func__); + if (*ppos) + return 0; + + len += snprintf(buf, SZ_128 - len, "HDCP state : %s\n", + sde_hdcp_state_name(display->hdcp_status)); + + if (copy_to_user(buff, buf, len)) + return -EFAULT; + + *ppos += len; + SDE_HDMI_DEBUG("%s - ", __func__); + return len; +} + static const struct file_operations dump_info_fops = { .open = simple_open, .read = _sde_hdmi_debugfs_dump_info_read, @@ -402,6 +536,26 @@ static const struct file_operations edid_vendor_name_fops = { .read = _sde_hdmi_edid_vendor_name_read, }; +static const struct file_operations hdcp_src_14_support_fops = { + .open = simple_open, + .read = _sde_hdmi_src_hdcp14_support_read, +}; + +static const struct file_operations hdcp_src_22_support_fops = { + .open = simple_open, + .read = _sde_hdmi_src_hdcp22_support_read, +}; + +static const struct file_operations hdcp_sink_22_support_fops = { + .open = simple_open, + .read = _sde_hdmi_sink_hdcp22_support_read, +}; + +static const struct file_operations sde_hdmi_hdcp_state_fops = { + .open = simple_open, + .read = _sde_hdmi_hdcp_state_read, +}; + static u64 _sde_hdmi_clip_valid_pclk(struct drm_display_mode *mode, u64 pclk_in) { u32 pclk_delta, pclk; @@ -422,6 +576,118 @@ static u64 _sde_hdmi_clip_valid_pclk(struct drm_display_mode *mode, u64 pclk_in) return pclk_clip; } +static void sde_hdmi_tx_hdcp_cb(void *ptr, enum sde_hdcp_states status) +{ + struct sde_hdmi *hdmi_ctrl = (struct sde_hdmi *)ptr; + struct hdmi *hdmi; + + if (!hdmi_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); + return; + } + + hdmi = hdmi_ctrl->ctrl.ctrl; + hdmi_ctrl->hdcp_status = status; + queue_delayed_work(hdmi->workq, &hdmi_ctrl->hdcp_cb_work, HZ/4); +} + +void sde_hdmi_hdcp_off(struct sde_hdmi *hdmi_ctrl) +{ + + if (!hdmi_ctrl) { + SDE_ERROR("%s: invalid input\n", __func__); + return; + } + + if (hdmi_ctrl->hdcp_ops) + hdmi_ctrl->hdcp_ops->off(hdmi_ctrl->hdcp_data); + + flush_delayed_work(&hdmi_ctrl->hdcp_cb_work); + + hdmi_ctrl->hdcp_ops = NULL; +} + +static void sde_hdmi_tx_hdcp_cb_work(struct work_struct *work) +{ + struct sde_hdmi *hdmi_ctrl = NULL; + struct delayed_work *dw = to_delayed_work(work); + int rc = 0; + struct hdmi *hdmi; + + hdmi_ctrl = container_of(dw, struct sde_hdmi, hdcp_cb_work); + if (!hdmi_ctrl) { + DEV_DBG("%s: invalid input\n", __func__); + return; + } + + hdmi = hdmi_ctrl->ctrl.ctrl; + + switch (hdmi_ctrl->hdcp_status) { + case HDCP_STATE_AUTHENTICATED: + hdmi_ctrl->auth_state = true; + + if (sde_hdmi_tx_is_panel_on(hdmi_ctrl) && + sde_hdmi_tx_is_stream_shareable(hdmi_ctrl)) { + rc = sde_hdmi_config_avmute(hdmi, false); + } + + if (hdmi_ctrl->hdcp1_use_sw_keys && + hdmi_ctrl->hdcp14_present) { + if (!hdmi_ctrl->hdcp22_present) + hdcp1_set_enc(true); + } + break; + case HDCP_STATE_AUTH_FAIL: + if (hdmi_ctrl->hdcp1_use_sw_keys && hdmi_ctrl->hdcp14_present) { + if (hdmi_ctrl->auth_state && !hdmi_ctrl->hdcp22_present) + hdcp1_set_enc(false); + } + + hdmi_ctrl->auth_state = false; + + if (sde_hdmi_tx_is_encryption_set(hdmi_ctrl) || + !sde_hdmi_tx_is_stream_shareable(hdmi_ctrl)) + rc = sde_hdmi_config_avmute(hdmi, true); + + if (sde_hdmi_tx_is_panel_on(hdmi_ctrl)) { + pr_debug("%s: Reauthenticating\n", __func__); + if (hdmi_ctrl->hdcp_ops && hdmi_ctrl->hdcp_data) { + rc = hdmi_ctrl->hdcp_ops->reauthenticate( + hdmi_ctrl->hdcp_data); + if (rc) + pr_err("%s: HDCP reauth failed. rc=%d\n", + __func__, rc); + } else + pr_err("%s: NULL HDCP Ops and Data\n", + __func__); + } else { + pr_debug("%s: Not reauthenticating. Cable not conn\n", + __func__); + } + + break; + case HDCP_STATE_AUTH_ENC_NONE: + hdmi_ctrl->enc_lvl = HDCP_STATE_AUTH_ENC_NONE; + if (sde_hdmi_tx_is_panel_on(hdmi_ctrl)) + rc = sde_hdmi_config_avmute(hdmi, false); + break; + case HDCP_STATE_AUTH_ENC_1X: + case HDCP_STATE_AUTH_ENC_2P2: + hdmi_ctrl->enc_lvl = hdmi_ctrl->hdcp_status; + + if (sde_hdmi_tx_is_panel_on(hdmi_ctrl) && + sde_hdmi_tx_is_stream_shareable(hdmi_ctrl)) { + rc = sde_hdmi_config_avmute(hdmi, false); + } else { + rc = sde_hdmi_config_avmute(hdmi, true); + } + break; + default: + break; + /* do nothing */ + } +} + /** * _sde_hdmi_update_pll_delta() - Update the HDMI pixel clock as per input ppm * @@ -439,13 +705,22 @@ static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm) u64 clip_pclk; int rc = 0; + mutex_lock(&display->display_lock); + if (!hdmi->power_on || !display->connected) { SDE_ERROR("HDMI display is not ready\n"); + mutex_unlock(&display->display_lock); + return -EINVAL; + } + + if (!display->pll_update_enable) { + SDE_ERROR("PLL update function is not enabled\n"); + mutex_unlock(&display->display_lock); return -EINVAL; } /* get current pclk */ - cur_pclk = hdmi->pixclock; + cur_pclk = hdmi->actual_pixclock; /* get desired pclk */ dst_pclk = cur_pclk * (1000000000 + ppm); do_div(dst_pclk, 1000000000); @@ -459,13 +734,16 @@ static int _sde_hdmi_update_pll_delta(struct sde_hdmi *display, s32 ppm) rc = clk_set_rate(hdmi->pwr_clks[0], clip_pclk); if (rc < 0) { - SDE_ERROR("PLL update failed, reset clock rate\n"); + SDE_ERROR("HDMI PLL update failed\n"); + mutex_unlock(&display->display_lock); return rc; } - hdmi->pixclock = clip_pclk; + hdmi->actual_pixclock = clip_pclk; } + mutex_unlock(&display->display_lock); + return rc; } @@ -501,12 +779,119 @@ static const struct file_operations pll_delta_fops = { .write = _sde_hdmi_debugfs_pll_delta_write, }; +/** + * _sde_hdmi_enable_pll_update() - Enable the HDMI PLL update function + * + * @enable: non-zero to enable PLL update function, 0 to disable. + * return: 0 on success, non-zero in case of failure. + * + */ +static int _sde_hdmi_enable_pll_update(struct sde_hdmi *display, s32 enable) +{ + struct hdmi *hdmi = display->ctrl.ctrl; + int rc = 0; + + mutex_lock(&display->display_lock); + + if (!hdmi->power_on || !display->connected) { + SDE_ERROR("HDMI display is not ready\n"); + mutex_unlock(&display->display_lock); + return -EINVAL; + } + + if (!enable && hdmi->actual_pixclock != hdmi->pixclock) { + /* reset pixel clock when disable */ + rc = clk_set_rate(hdmi->pwr_clks[0], hdmi->pixclock); + if (rc < 0) { + SDE_ERROR("reset clock rate failed\n"); + mutex_unlock(&display->display_lock); + return rc; + } + } + hdmi->actual_pixclock = hdmi->pixclock; + + display->pll_update_enable = !!enable; + + mutex_unlock(&display->display_lock); + + SDE_DEBUG("HDMI PLL update: %s\n", + display->pll_update_enable ? "enable" : "disable"); + + return rc; +} + +static ssize_t _sde_hdmi_debugfs_pll_enable_read(struct file *file, + char __user *buff, size_t count, loff_t *ppos) +{ + struct sde_hdmi *display = file->private_data; + char *buf; + u32 len = 0; + + if (!display) + return -ENODEV; + + if (*ppos) + return 0; + + buf = kzalloc(SZ_1K, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += snprintf(buf, SZ_4K, "%s\n", + display->pll_update_enable ? "enable" : "disable"); + + if (copy_to_user(buff, buf, len)) { + kfree(buf); + return -EFAULT; + } + + *ppos += len; + + kfree(buf); + return len; +} + +static ssize_t _sde_hdmi_debugfs_pll_enable_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct sde_hdmi *display = file->private_data; + char buf[10]; + int enable = 0; + + if (!display) + return -ENODEV; + + if (count >= sizeof(buf)) + return -EFAULT; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + buf[count] = 0; /* end of string */ + + if (kstrtoint(buf, 0, &enable)) + return -EFAULT; + + _sde_hdmi_enable_pll_update(display, enable); + + return count; +} + +static const struct file_operations pll_enable_fops = { + .open = simple_open, + .read = _sde_hdmi_debugfs_pll_enable_read, + .write = _sde_hdmi_debugfs_pll_enable_write, +}; + static int _sde_hdmi_debugfs_init(struct sde_hdmi *display) { int rc = 0; struct dentry *dir, *dump_file, *edid_modes; struct dentry *edid_vsdb_info, *edid_hdr_info, *edid_hfvsdb_info; - struct dentry *edid_vcdb_info, *edid_vendor_name, *pll_file; + struct dentry *edid_vcdb_info, *edid_vendor_name; + struct dentry *src_hdcp14_support, *src_hdcp22_support; + struct dentry *sink_hdcp22_support, *hdmi_hdcp_state; + struct dentry *pll_delta_file, *pll_enable_file; dir = debugfs_create_dir(display->name, NULL); if (!dir) { @@ -528,18 +913,30 @@ static int _sde_hdmi_debugfs_init(struct sde_hdmi *display) goto error_remove_dir; } - pll_file = debugfs_create_file("pll_delta", + pll_delta_file = debugfs_create_file("pll_delta", 0644, dir, display, &pll_delta_fops); - if (IS_ERR_OR_NULL(pll_file)) { - rc = PTR_ERR(pll_file); + if (IS_ERR_OR_NULL(pll_delta_file)) { + rc = PTR_ERR(pll_delta_file); SDE_ERROR("[%s]debugfs create pll_delta file failed, rc=%d\n", display->name, rc); goto error_remove_dir; } + pll_enable_file = debugfs_create_file("pll_enable", + 0644, + dir, + display, + &pll_enable_fops); + if (IS_ERR_OR_NULL(pll_enable_file)) { + rc = PTR_ERR(pll_enable_file); + SDE_ERROR("[%s]debugfs create pll_enable file failed, rc=%d\n", + display->name, rc); + goto error_remove_dir; + } + edid_modes = debugfs_create_file("edid_modes", 0444, dir, @@ -617,6 +1014,58 @@ static int _sde_hdmi_debugfs_init(struct sde_hdmi *display) goto error_remove_dir; } + src_hdcp14_support = debugfs_create_file("src_hdcp14_support", + 0444, + dir, + display, + &hdcp_src_14_support_fops); + + if (IS_ERR_OR_NULL(src_hdcp14_support)) { + rc = PTR_ERR(src_hdcp14_support); + SDE_ERROR("[%s]debugfs create file failed, rc=%d\n", + display->name, rc); + goto error_remove_dir; + } + + src_hdcp22_support = debugfs_create_file("src_hdcp22_support", + 0444, + dir, + display, + &hdcp_src_22_support_fops); + + if (IS_ERR_OR_NULL(src_hdcp22_support)) { + rc = PTR_ERR(src_hdcp22_support); + SDE_ERROR("[%s]debugfs create file failed, rc=%d\n", + display->name, rc); + goto error_remove_dir; + } + + sink_hdcp22_support = debugfs_create_file("sink_hdcp22_support", + 0444, + dir, + display, + &hdcp_sink_22_support_fops); + + if (IS_ERR_OR_NULL(sink_hdcp22_support)) { + rc = PTR_ERR(sink_hdcp22_support); + SDE_ERROR("[%s]debugfs create file failed, rc=%d\n", + display->name, rc); + goto error_remove_dir; + } + + hdmi_hdcp_state = debugfs_create_file("hdmi_hdcp_state", + 0444, + dir, + display, + &sde_hdmi_hdcp_state_fops); + + if (IS_ERR_OR_NULL(hdmi_hdcp_state)) { + rc = PTR_ERR(hdmi_hdcp_state); + SDE_ERROR("[%s]debugfs create file failed, rc=%d\n", + display->name, rc); + goto error_remove_dir; + } + display->root = dir; return rc; error_remove_dir: @@ -907,6 +1356,34 @@ static void _sde_hdmi_hdp_disable(struct sde_hdmi *sde_hdmi) } } +static void _sde_hdmi_cec_update_phys_addr(struct sde_hdmi *display) +{ + struct edid *edid = display->edid_ctrl->edid; + + if (edid) + cec_notifier_set_phys_addr_from_edid(display->notifier, edid); + else + cec_notifier_set_phys_addr(display->notifier, + CEC_PHYS_ADDR_INVALID); + +} + +static void _sde_hdmi_init_ddc(struct sde_hdmi *display, struct hdmi *hdmi) +{ + display->ddc_ctrl.io = &display->io[HDMI_TX_CORE_IO]; + init_completion(&display->ddc_ctrl.rx_status_done); +} + +static void _sde_hdmi_map_regs(struct sde_hdmi *display, struct hdmi *hdmi) +{ + display->io[HDMI_TX_CORE_IO].base = hdmi->mmio; + display->io[HDMI_TX_CORE_IO].len = hdmi->mmio_len; + display->io[HDMI_TX_QFPROM_IO].base = hdmi->qfprom_mmio; + display->io[HDMI_TX_QFPROM_IO].len = hdmi->qfprom_mmio_len; + display->io[HDMI_TX_HDCP_IO].base = hdmi->hdcp_mmio; + display->io[HDMI_TX_HDCP_IO].len = hdmi->hdcp_mmio_len; +} + static void _sde_hdmi_hotplug_work(struct work_struct *work) { struct sde_hdmi *sde_hdmi = @@ -936,6 +1413,7 @@ static void _sde_hdmi_hotplug_work(struct work_struct *work) sde_free_edid((void **)&sde_hdmi->edid_ctrl); drm_helper_hpd_irq_event(connector->dev); + _sde_hdmi_cec_update_phys_addr(sde_hdmi); } static void _sde_hdmi_connector_irq(struct sde_hdmi *sde_hdmi) @@ -982,26 +1460,39 @@ static void _sde_hdmi_cec_irq(struct sde_hdmi *sde_hdmi) static irqreturn_t _sde_hdmi_irq(int irq, void *dev_id) { - struct sde_hdmi *sde_hdmi = dev_id; + struct sde_hdmi *display = dev_id; struct hdmi *hdmi; - if (!sde_hdmi || !sde_hdmi->ctrl.ctrl) { - SDE_ERROR("sde_hdmi=%p or hdmi is NULL\n", sde_hdmi); + if (!display || !display->ctrl.ctrl) { + SDE_ERROR("sde_hdmi=%pK or hdmi is NULL\n", display); return IRQ_NONE; } - hdmi = sde_hdmi->ctrl.ctrl; + + hdmi = display->ctrl.ctrl; /* Process HPD: */ - _sde_hdmi_connector_irq(sde_hdmi); + _sde_hdmi_connector_irq(display); + + /* Process Scrambling ISR */ + sde_hdmi_ddc_scrambling_isr((void *)display); + + /* Process DDC2 */ + sde_hdmi_ddc_hdcp2p2_isr((void *)display); /* Process DDC: */ hdmi_i2c_irq(hdmi->i2c); /* Process HDCP: */ - if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported) - hdmi_hdcp_ctrl_irq(hdmi->hdcp_ctrl); + if (display->hdcp_ops && display->hdcp_data) { + if (display->hdcp_ops->isr) { + if (display->hdcp_ops->isr( + display->hdcp_data)) + DEV_ERR("%s: hdcp_1x_isr failed\n", + __func__); + } + } /* Process CEC: */ - _sde_hdmi_cec_irq(sde_hdmi); + _sde_hdmi_cec_irq(display); return IRQ_HANDLED; } @@ -1177,84 +1668,8 @@ void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on) power_on ? "Enable" : "Disable", ctrl); } -int sde_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset, - u8 *data, u16 data_len) -{ - int rc; - int retry = 5; - struct i2c_msg msgs[] = { - { - .addr = addr >> 1, - .flags = 0, - .len = 1, - .buf = &offset, - }, { - .addr = addr >> 1, - .flags = I2C_M_RD, - .len = data_len, - .buf = data, - } - }; - - SDE_HDMI_DEBUG("Start DDC read"); - retry: - rc = i2c_transfer(hdmi->i2c, msgs, 2); - - retry--; - if (rc == 2) - rc = 0; - else if (retry > 0) - goto retry; - else - rc = -EIO; - - SDE_HDMI_DEBUG("End DDC read %d", rc); - - return rc; -} - #define DDC_WRITE_MAX_BYTE_NUM 32 -int sde_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset, - u8 *data, u16 data_len) -{ - int rc; - int retry = 10; - u8 buf[DDC_WRITE_MAX_BYTE_NUM]; - struct i2c_msg msgs[] = { - { - .addr = addr >> 1, - .flags = 0, - .len = 1, - } - }; - - SDE_HDMI_DEBUG("Start DDC write"); - if (data_len > (DDC_WRITE_MAX_BYTE_NUM - 1)) { - SDE_ERROR("%s: write size too big\n", __func__); - return -ERANGE; - } - - buf[0] = offset; - memcpy(&buf[1], data, data_len); - msgs[0].buf = buf; - msgs[0].len = data_len + 1; - retry: - rc = i2c_transfer(hdmi->i2c, msgs, 1); - - retry--; - if (rc == 1) - rc = 0; - else if (retry > 0) - goto retry; - else - rc = -EIO; - - SDE_HDMI_DEBUG("End DDC write %d", rc); - - return rc; -} - int sde_hdmi_scdc_read(struct hdmi *hdmi, u32 data_type, u32 *val) { int rc = 0; @@ -1311,7 +1726,8 @@ int sde_hdmi_scdc_read(struct hdmi *hdmi, u32 data_type, u32 *val) break; } - rc = sde_hdmi_ddc_read(hdmi, dev_addr, offset, data_buf, data_len); + rc = hdmi_ddc_read(hdmi, dev_addr, offset, data_buf, + data_len, true); if (rc) { SDE_ERROR("DDC Read failed for %d\n", data_type); return rc; @@ -1383,8 +1799,8 @@ int sde_hdmi_scdc_write(struct hdmi *hdmi, u32 data_type, u32 val) dev_addr = 0xA8; data_len = 1; offset = HDMI_SCDC_TMDS_CONFIG; - rc = sde_hdmi_ddc_read(hdmi, dev_addr, offset, &read_val, - data_len); + rc = hdmi_ddc_read(hdmi, dev_addr, offset, &read_val, + data_len, true); if (rc) { SDE_ERROR("scdc read failed\n"); return rc; @@ -1408,7 +1824,8 @@ int sde_hdmi_scdc_write(struct hdmi *hdmi, u32 data_type, u32 val) return -EINVAL; } - rc = sde_hdmi_ddc_write(hdmi, dev_addr, offset, data_buf, data_len); + rc = hdmi_ddc_write(hdmi, dev_addr, offset, data_buf, + data_len, true); if (rc) { SDE_ERROR("DDC Read failed for %d\n", data_type); return rc; @@ -1442,14 +1859,114 @@ int sde_hdmi_get_info(struct msm_display_info *info, MSM_DISPLAY_CAP_EDID | MSM_DISPLAY_CAP_VID_MODE; } info->is_connected = hdmi_display->connected; - info->max_width = 4096; - info->max_height = 2160; + info->max_width = HDMI_DISPLAY_MAX_WIDTH; + info->max_height = HDMI_DISPLAY_MAX_HEIGHT; info->compression = MSM_DISPLAY_COMPRESS_NONE; mutex_unlock(&hdmi_display->display_lock); return rc; } +static void sde_hdmi_panel_set_hdr_infoframe(struct sde_hdmi *display, +struct drm_msm_ext_panel_hdr_metadata *hdr_meta) +{ + u32 packet_payload = 0; + u32 packet_header = 0; + u32 packet_control = 0; + u32 const type_code = 0x87; + u32 const version = 0x01; + u32 const length = 0x1a; + u32 const descriptor_id = 0x00; + struct hdmi *hdmi; + struct drm_connector *connector; + + if (!display || !hdr_meta) { + SDE_ERROR("invalid input\n"); + return; + } + + hdmi = display->ctrl.ctrl; + connector = display->ctrl.ctrl->connector; + + if (!hdmi || !connector) { + SDE_ERROR("invalid input\n"); + return; + } + + if (!connector->hdr_supported) { + SDE_ERROR("Sink does not support HDR\n"); + return; + } + + /* Setup Packet header and payload */ + packet_header = type_code | (version << 8) | (length << 16); + hdmi_write(hdmi, HDMI_GENERIC0_HDR, packet_header); + + packet_payload = (hdr_meta->eotf << 8); + if (connector->hdr_metadata_type_one) { + packet_payload |= (descriptor_id << 16) + | (HDMI_GET_LSB(hdr_meta->display_primaries_x[0]) + << 24); + hdmi_write(hdmi, HDMI_GENERIC0_0, packet_payload); + } else { + pr_debug("Metadata Type 1 not supported\n"); + hdmi_write(hdmi, HDMI_GENERIC0_0, packet_payload); + goto enable_packet_control; + } + + packet_payload = + (HDMI_GET_MSB(hdr_meta->display_primaries_x[0])) + | (HDMI_GET_LSB(hdr_meta->display_primaries_y[0]) << 8) + | (HDMI_GET_MSB(hdr_meta->display_primaries_y[0]) << 16) + | (HDMI_GET_LSB(hdr_meta->display_primaries_x[1]) << 24); + hdmi_write(hdmi, HDMI_GENERIC0_1, packet_payload); + + packet_payload = + (HDMI_GET_MSB(hdr_meta->display_primaries_x[1])) + | (HDMI_GET_LSB(hdr_meta->display_primaries_y[1]) << 8) + | (HDMI_GET_MSB(hdr_meta->display_primaries_y[1]) << 16) + | (HDMI_GET_LSB(hdr_meta->display_primaries_x[2]) << 24); + hdmi_write(hdmi, HDMI_GENERIC0_2, packet_payload); + + packet_payload = + (HDMI_GET_MSB(hdr_meta->display_primaries_x[2])) + | (HDMI_GET_LSB(hdr_meta->display_primaries_y[2]) << 8) + | (HDMI_GET_MSB(hdr_meta->display_primaries_y[2]) << 16) + | (HDMI_GET_LSB(hdr_meta->white_point_x) << 24); + hdmi_write(hdmi, HDMI_GENERIC0_3, packet_payload); + + packet_payload = + (HDMI_GET_MSB(hdr_meta->white_point_x)) + | (HDMI_GET_LSB(hdr_meta->white_point_y) << 8) + | (HDMI_GET_MSB(hdr_meta->white_point_y) << 16) + | (HDMI_GET_LSB(hdr_meta->max_luminance) << 24); + hdmi_write(hdmi, HDMI_GENERIC0_4, packet_payload); + + packet_payload = + (HDMI_GET_MSB(hdr_meta->max_luminance)) + | (HDMI_GET_LSB(hdr_meta->min_luminance) << 8) + | (HDMI_GET_MSB(hdr_meta->min_luminance) << 16) + | (HDMI_GET_LSB(hdr_meta->max_content_light_level) << 24); + hdmi_write(hdmi, HDMI_GENERIC0_5, packet_payload); + + packet_payload = + (HDMI_GET_MSB(hdr_meta->max_content_light_level)) + | (HDMI_GET_LSB(hdr_meta->max_average_light_level) << 8) + | (HDMI_GET_MSB(hdr_meta->max_average_light_level) << 16); + hdmi_write(hdmi, HDMI_GENERIC0_6, packet_payload); + +enable_packet_control: + /* + * GENERIC0_LINE | GENERIC0_CONT | GENERIC0_SEND + * Setup HDMI TX generic packet control + * Enable this packet to transmit every frame + * Enable HDMI TX engine to transmit Generic packet 1 + */ + packet_control = hdmi_read(hdmi, HDMI_GEN_PKT_CTRL); + packet_control |= BIT(0) | BIT(1) | BIT(2) | BIT(16); + hdmi_write(hdmi, HDMI_GEN_PKT_CTRL, packet_control); +} + int sde_hdmi_set_property(struct drm_connector *connector, struct drm_connector_state *state, int property_index, @@ -1461,17 +1978,42 @@ int sde_hdmi_set_property(struct drm_connector *connector, if (!connector || !display) { SDE_ERROR("connector=%pK or display=%pK is NULL\n", connector, display); - return 0; + return -EINVAL; } SDE_DEBUG("\n"); - if (property_index == CONNECTOR_PROP_PLL_DELTA) + if (property_index == CONNECTOR_PROP_PLL_ENABLE) + rc = _sde_hdmi_enable_pll_update(display, value); + else if (property_index == CONNECTOR_PROP_PLL_DELTA) rc = _sde_hdmi_update_pll_delta(display, value); return rc; } +int sde_hdmi_get_property(struct drm_connector *connector, + struct drm_connector_state *state, + int property_index, + uint64_t *value, + void *display) +{ + struct sde_hdmi *hdmi_display = display; + int rc = 0; + + if (!connector || !hdmi_display) { + SDE_ERROR("connector=%pK or display=%pK is NULL\n", + connector, hdmi_display); + return -EINVAL; + } + + mutex_lock(&hdmi_display->display_lock); + if (property_index == CONNECTOR_PROP_PLL_ENABLE) + *value = hdmi_display->pll_update_enable ? 1 : 0; + mutex_unlock(&hdmi_display->display_lock); + + return rc; +} + u32 sde_hdmi_get_num_of_displays(void) { u32 count = 0; @@ -1525,6 +2067,141 @@ int sde_hdmi_connector_pre_deinit(struct drm_connector *connector, return 0; } +static void _sde_hdmi_get_tx_version(struct sde_hdmi *sde_hdmi) +{ + struct hdmi *hdmi = sde_hdmi->ctrl.ctrl; + + sde_hdmi->hdmi_tx_version = hdmi_read(hdmi, REG_HDMI_VERSION); + sde_hdmi->hdmi_tx_major_version = + SDE_GET_MAJOR_VER(sde_hdmi->hdmi_tx_version); + + switch (sde_hdmi->hdmi_tx_major_version) { + case (HDMI_TX_VERSION_3): + sde_hdmi->max_pclk_khz = HDMI_TX_3_MAX_PCLK_RATE; + break; + case (HDMI_TX_VERSION_4): + sde_hdmi->max_pclk_khz = HDMI_TX_4_MAX_PCLK_RATE; + break; + default: + sde_hdmi->max_pclk_khz = HDMI_DEFAULT_MAX_PCLK_RATE; + break; + } + SDE_DEBUG("sde_hdmi->hdmi_tx_version = 0x%x\n", + sde_hdmi->hdmi_tx_version); + SDE_DEBUG("sde_hdmi->hdmi_tx_major_version = 0x%x\n", + sde_hdmi->hdmi_tx_major_version); + SDE_DEBUG("sde_hdmi->max_pclk_khz = 0x%x\n", + sde_hdmi->max_pclk_khz); +} + +static int sde_hdmi_tx_check_capability(struct sde_hdmi *sde_hdmi) +{ + u32 hdmi_disabled, hdcp_disabled, reg_val; + int ret = 0; + struct hdmi *hdmi = sde_hdmi->ctrl.ctrl; + + /* check if hdmi and hdcp are disabled */ + if (sde_hdmi->hdmi_tx_major_version < HDMI_TX_VERSION_4) { + hdcp_disabled = hdmi_qfprom_read(hdmi, + QFPROM_RAW_FEAT_CONFIG_ROW0_LSB) & BIT(31); + + hdmi_disabled = hdmi_qfprom_read(hdmi, + QFPROM_RAW_FEAT_CONFIG_ROW0_MSB) & BIT(0); + } else { + reg_val = hdmi_qfprom_read(hdmi, + QFPROM_RAW_FEAT_CONFIG_ROW0_LSB + QFPROM_RAW_VERSION_4); + hdcp_disabled = reg_val & BIT(12); + + hdmi_disabled = reg_val & BIT(13); + + reg_val = hdmi_qfprom_read(hdmi, SEC_CTRL_HW_VERSION); + + SDE_DEBUG("SEC_CTRL_HW_VERSION reg_val = 0x%x\n", reg_val); + /* + * With HDCP enabled on capable hardware, check if HW + * or SW keys should be used. + */ + if (!hdcp_disabled && (reg_val >= HDCP_SEL_MIN_SEC_VERSION)) { + reg_val = hdmi_qfprom_read(hdmi, + QFPROM_RAW_FEAT_CONFIG_ROW0_MSB + + QFPROM_RAW_VERSION_4); + + if (!(reg_val & BIT(23))) + sde_hdmi->hdcp1_use_sw_keys = true; + } + } + + SDE_DEBUG("%s: Features <HDMI:%s, HDCP:%s>\n", __func__, + hdmi_disabled ? "OFF" : "ON", + hdcp_disabled ? "OFF" : "ON"); + + if (hdmi_disabled) { + DEV_ERR("%s: HDMI disabled\n", __func__); + ret = -ENODEV; + goto end; + } + + sde_hdmi->hdcp14_present = !hdcp_disabled; + + end: + return ret; +} /* hdmi_tx_check_capability */ + +static int _sde_hdmi_init_hdcp(struct sde_hdmi *hdmi_ctrl) +{ + struct sde_hdcp_init_data hdcp_init_data; + void *hdcp_data; + int rc = 0; + struct hdmi *hdmi; + + if (!hdmi_ctrl) { + SDE_ERROR("sde_hdmi is NULL\n"); + return -EINVAL; + } + + hdmi = hdmi_ctrl->ctrl.ctrl; + hdcp_init_data.phy_addr = hdmi->mmio_phy_addr; + hdcp_init_data.core_io = &hdmi_ctrl->io[HDMI_TX_CORE_IO]; + hdcp_init_data.qfprom_io = &hdmi_ctrl->io[HDMI_TX_QFPROM_IO]; + hdcp_init_data.hdcp_io = &hdmi_ctrl->io[HDMI_TX_HDCP_IO]; + hdcp_init_data.mutex = &hdmi_ctrl->hdcp_mutex; + hdcp_init_data.workq = hdmi->workq; + hdcp_init_data.notify_status = sde_hdmi_tx_hdcp_cb; + hdcp_init_data.cb_data = (void *)hdmi_ctrl; + hdcp_init_data.hdmi_tx_ver = hdmi_ctrl->hdmi_tx_major_version; + hdcp_init_data.sec_access = true; + hdcp_init_data.client_id = HDCP_CLIENT_HDMI; + hdcp_init_data.ddc_ctrl = &hdmi_ctrl->ddc_ctrl; + + if (hdmi_ctrl->hdcp14_present) { + hdcp_data = sde_hdcp_1x_init(&hdcp_init_data); + + if (IS_ERR_OR_NULL(hdcp_data)) { + DEV_ERR("%s: hdcp 1.4 init failed\n", __func__); + rc = -EINVAL; + kfree(hdcp_data); + goto end; + } else { + hdmi_ctrl->hdcp_feat_data[SDE_HDCP_1x] = hdcp_data; + SDE_HDMI_DEBUG("%s: HDCP 1.4 initialized\n", __func__); + } + } + + hdcp_data = sde_hdmi_hdcp2p2_init(&hdcp_init_data); + + if (IS_ERR_OR_NULL(hdcp_data)) { + DEV_ERR("%s: hdcp 2.2 init failed\n", __func__); + rc = -EINVAL; + goto end; + } else { + hdmi_ctrl->hdcp_feat_data[SDE_HDCP_2P2] = hdcp_data; + SDE_HDMI_DEBUG("%s: HDCP 2.2 initialized\n", __func__); + } + +end: + return rc; +} + int sde_hdmi_connector_post_init(struct drm_connector *connector, void *info, void *display) @@ -1557,6 +2234,37 @@ int sde_hdmi_connector_post_init(struct drm_connector *connector, if (rc) SDE_ERROR("failed to enable HPD: %d\n", rc); + _sde_hdmi_get_tx_version(sde_hdmi); + + sde_hdmi_tx_check_capability(sde_hdmi); + + _sde_hdmi_init_hdcp(sde_hdmi); + + return rc; +} + +int sde_hdmi_start_hdcp(struct drm_connector *connector) +{ + int rc; + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + struct hdmi *hdmi = display->ctrl.ctrl; + + if (!hdmi) { + SDE_ERROR("%s: invalid input\n", __func__); + return -EINVAL; + } + + if (!sde_hdmi_tx_is_hdcp_enabled(display)) + return 0; + + if (sde_hdmi_tx_is_encryption_set(display)) + sde_hdmi_config_avmute(hdmi, true); + + rc = display->hdcp_ops->authenticate(display->hdcp_data); + if (rc) + SDE_ERROR("%s: hdcp auth failed. rc=%d\n", __func__, rc); + return rc; } @@ -1595,6 +2303,17 @@ sde_hdmi_connector_detect(struct drm_connector *connector, return status; } +int sde_hdmi_pre_kickoff(struct drm_connector *connector, + void *display, + struct msm_display_kickoff_params *params) +{ + + sde_hdmi_panel_set_hdr_infoframe(display, + params->hdr_metadata); + + return 0; +} + int sde_hdmi_connector_get_modes(struct drm_connector *connector, void *display) { struct sde_hdmi *hdmi_display = (struct sde_hdmi *)display; @@ -1673,10 +2392,34 @@ int sde_hdmi_dev_deinit(struct sde_hdmi *display) SDE_ERROR("Invalid params\n"); return -EINVAL; } + if (display->hdcp_feat_data[SDE_HDCP_1x]) + sde_hdcp_1x_deinit(display->hdcp_feat_data[SDE_HDCP_1x]); + + if (display->hdcp_feat_data[SDE_HDCP_2P2]) + sde_hdmi_hdcp2p2_deinit(display->hdcp_feat_data[SDE_HDCP_2P2]); return 0; } +static int _sde_hdmi_cec_init(struct sde_hdmi *display) +{ + struct platform_device *pdev = display->pdev; + + display->notifier = cec_notifier_get(&pdev->dev); + if (!display->notifier) { + SDE_ERROR("CEC notifier get failed\n"); + return -ENOMEM; + } + + return 0; +} + +static void _sde_hdmi_cec_deinit(struct sde_hdmi *display) +{ + cec_notifier_set_phys_addr(display->notifier, CEC_PHYS_ADDR_INVALID); + cec_notifier_put(display->notifier); +} + static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) { int rc = 0; @@ -1715,7 +2458,14 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) if (rc) { SDE_ERROR("[%s]Ext Disp init failed, rc=%d\n", display->name, rc); - goto error; + goto ext_error; + } + + rc = _sde_hdmi_cec_init(display); + if (rc) { + SDE_ERROR("[%s]CEC init failed, rc=%d\n", + display->name, rc); + goto ext_error; } display->edid_ctrl = sde_edid_init(); @@ -1723,16 +2473,27 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data) SDE_ERROR("[%s]sde edid init failed\n", display->name); rc = -ENOMEM; - goto error; + goto cec_error; } display_ctrl = &display->ctrl; display_ctrl->ctrl = priv->hdmi; display->drm_dev = drm; + _sde_hdmi_map_regs(display, priv->hdmi); + _sde_hdmi_init_ddc(display, priv->hdmi); + + display->enc_lvl = HDCP_STATE_AUTH_ENC_NONE; + + INIT_DELAYED_WORK(&display->hdcp_cb_work, + sde_hdmi_tx_hdcp_cb_work); + mutex_init(&display->hdcp_mutex); mutex_unlock(&display->display_lock); return rc; -error: + +cec_error: + (void)_sde_hdmi_cec_deinit(display); +ext_error: (void)_sde_hdmi_debugfs_deinit(display); debug_error: mutex_unlock(&display->display_lock); @@ -1758,6 +2519,7 @@ static void sde_hdmi_unbind(struct device *dev, struct device *master, mutex_lock(&display->display_lock); (void)_sde_hdmi_debugfs_deinit(display); (void)sde_edid_deinit((void **)&display->edid_ctrl); + (void)_sde_hdmi_cec_deinit(display); display->drm_dev = NULL; mutex_unlock(&display->display_lock); } diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h index 54506da4f9b0..6b9bcfec031b 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -20,12 +20,18 @@ #include <linux/debugfs.h> #include <linux/of_device.h> #include <linux/msm_ext_display.h> +#include <linux/hdcp_qseecom.h> #include <drm/drmP.h> #include <drm/drm_crtc.h> +#include <media/cec-notifier.h> #include "hdmi.h" - +#include "sde_kms.h" +#include "sde_connector.h" +#include "msm_drv.h" #include "sde_edid_parser.h" +#include "sde_hdmi_util.h" +#include "sde_hdcp.h" #ifdef HDMI_DEBUG_ENABLE #define SDE_HDMI_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args) @@ -33,6 +39,10 @@ #define SDE_HDMI_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args) #endif +/* HW Revisions for different SDE targets */ +#define SDE_GET_MAJOR_VER(rev)((rev) >> 28) +#define SDE_GET_MINOR_VER(rev)(((rev) >> 16) & 0xFFF) + /** * struct sde_hdmi_info - defines hdmi display properties * @display_type: Display type as defined by device tree. @@ -68,6 +78,18 @@ struct sde_hdmi_ctrl { u32 hdmi_ctrl_idx; }; +enum hdmi_tx_io_type { + HDMI_TX_CORE_IO, + HDMI_TX_QFPROM_IO, + HDMI_TX_HDCP_IO, + HDMI_TX_MAX_IO +}; + +enum hdmi_tx_feature_type { + SDE_HDCP_1x, + SDE_HDCP_2P2 +}; + /** * struct sde_hdmi - hdmi display information * @pdev: Pointer to platform device. @@ -87,6 +109,8 @@ struct sde_hdmi_ctrl { * @codec_ready: If audio codec is ready. * @client_notify_pending: If there is client notification pending. * @irq_domain: IRQ domain structure. + * @pll_update_enable: if it's allowed to update HDMI PLL ppm. + * @notifier: CEC notifider to convey physical address information. * @root: Debug fs root entry. */ struct sde_hdmi { @@ -97,7 +121,7 @@ struct sde_hdmi { const char *display_type; struct list_head list; struct mutex display_lock; - + struct mutex hdcp_mutex; struct sde_hdmi_ctrl ctrl; struct platform_device *ext_pdev; @@ -110,13 +134,36 @@ struct sde_hdmi { struct drm_display_mode mode; bool connected; bool is_tpg_enabled; - + u32 hdmi_tx_version; + u32 hdmi_tx_major_version; + u32 max_pclk_khz; + bool hdcp1_use_sw_keys; + u32 hdcp14_present; + u32 hdcp22_present; + u8 hdcp_status; + u32 enc_lvl; + bool auth_state; + bool sink_hdcp22_support; + bool src_hdcp22_support; + + /*hold final data + *based on hdcp support + */ + void *hdcp_data; + /*hold hdcp init data*/ + void *hdcp_feat_data[2]; + struct sde_hdcp_ops *hdcp_ops; + struct sde_hdmi_tx_ddc_ctrl ddc_ctrl; struct work_struct hpd_work; bool codec_ready; bool client_notify_pending; struct irq_domain *irq_domain; + struct cec_notifier *notifier; + bool pll_update_enable; + struct delayed_work hdcp_cb_work; + struct dss_io_data io[HDMI_TX_MAX_IO]; /* DEBUG FS */ struct dentry *root; }; @@ -141,6 +188,11 @@ enum hdmi_tx_scdc_access_type { #define HDMI_KHZ_TO_HZ 1000 #define HDMI_MHZ_TO_HZ 1000000 + +/* Maximum pixel clock rates for hdmi tx */ +#define HDMI_DEFAULT_MAX_PCLK_RATE 148500 +#define HDMI_TX_3_MAX_PCLK_RATE 297000 +#define HDMI_TX_4_MAX_PCLK_RATE 600000 /** * hdmi_tx_ddc_timer_type() - hdmi DDC timer functionalities. */ @@ -295,6 +347,22 @@ int sde_hdmi_set_property(struct drm_connector *connector, void *display); /** + * sde_hdmi_get_property() - get the connector properties + * @connector: Handle to the connector. + * @state: Handle to the connector state. + * @property_index: property index. + * @value: property value. + * @display: Handle to the display. + * + * Return: error code. + */ +int sde_hdmi_get_property(struct drm_connector *connector, + struct drm_connector_state *state, + int property_index, + uint64_t *value, + void *display); + +/** * sde_hdmi_bridge_init() - init sde hdmi bridge * @hdmi: Handle to the hdmi. * @@ -312,32 +380,6 @@ struct drm_bridge *sde_hdmi_bridge_init(struct hdmi *hdmi); void sde_hdmi_set_mode(struct hdmi *hdmi, bool power_on); /** - * sde_hdmi_ddc_read() - common hdmi ddc read API. - * @hdmi: Handle to the hdmi. - * @addr: Command address. - * @offset: Command offset. - * @data: Data buffer for read back. - * @data_len: Data buffer length. - * - * Return: error code. - */ -int sde_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset, - u8 *data, u16 data_len); - -/** - * sde_hdmi_ddc_write() - common hdmi ddc write API. - * @hdmi: Handle to the hdmi. - * @addr: Command address. - * @offset: Command offset. - * @data: Data buffer for write. - * @data_len: Data buffer length. - * - * Return: error code. - */ -int sde_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset, - u8 *data, u16 data_len); - -/** * sde_hdmi_scdc_read() - hdmi 2.0 ddc read API. * @hdmi: Handle to the hdmi. * @data_type: DDC data type, refer to enum hdmi_tx_scdc_access_type. @@ -403,6 +445,24 @@ void sde_hdmi_notify_clients(struct sde_hdmi *display, bool connected); void sde_hdmi_ack_state(struct drm_connector *connector, enum drm_connector_status status); +bool sde_hdmi_tx_is_hdcp_enabled(struct sde_hdmi *hdmi_ctrl); +bool sde_hdmi_tx_is_encryption_set(struct sde_hdmi *hdmi_ctrl); +bool sde_hdmi_tx_is_stream_shareable(struct sde_hdmi *hdmi_ctrl); +bool sde_hdmi_tx_is_panel_on(struct sde_hdmi *hdmi_ctrl); +int sde_hdmi_start_hdcp(struct drm_connector *connector); +void sde_hdmi_hdcp_off(struct sde_hdmi *hdmi_ctrl); + + +/* + * sde_hdmi_pre_kickoff - program kickoff-time features + * @display: Pointer to private display structure + * @params: Parameters for kickoff-time programming + * Returns: Zero on success + */ +int sde_hdmi_pre_kickoff(struct drm_connector *connector, + void *display, + struct msm_display_kickoff_params *params); + #else /*#ifdef CONFIG_DRM_SDE_HDMI*/ static inline u32 sde_hdmi_get_num_of_displays(void) @@ -461,12 +521,42 @@ static inline int sde_hdmi_dev_deinit(struct sde_hdmi *display) return 0; } +bool hdmi_tx_is_hdcp_enabled(struct sde_hdmi *hdmi_ctrl) +{ + return false; +} + +bool sde_hdmi_tx_is_encryption_set(struct sde_hdmi *hdmi_ctrl) +{ + return false; +} + +bool sde_hdmi_tx_is_stream_shareable(struct sde_hdmi *hdmi_ctrl) +{ + return false; +} + +bool sde_hdmi_tx_is_panel_on(struct sde_hdmi *hdmi_ctrl) +{ + return false; +} + static inline int sde_hdmi_drm_init(struct sde_hdmi *display, struct drm_encoder *enc) { return 0; } +int sde_hdmi_start_hdcp(struct drm_connector *connector) +{ + return 0; +} + +void sde_hdmi_hdcp_off(struct sde_hdmi *hdmi_ctrl) +{ + +} + static inline int sde_hdmi_drm_deinit(struct sde_hdmi *display) { return 0; diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c index 48a3a9316a41..d6213dc0a4aa 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c @@ -355,37 +355,3 @@ void sde_hdmi_audio_off(struct hdmi *hdmi) SDE_DEBUG("HDMI Audio: Disabled\n"); } -int sde_hdmi_config_avmute(struct hdmi *hdmi, bool set) -{ - u32 av_mute_status; - bool av_pkt_en = false; - - if (!hdmi) { - SDE_ERROR("invalid HDMI Ctrl\n"); - return -ENODEV; - } - - av_mute_status = hdmi_read(hdmi, HDMI_GC); - - if (set) { - if (!(av_mute_status & BIT(0))) { - hdmi_write(hdmi, HDMI_GC, av_mute_status | BIT(0)); - av_pkt_en = true; - } - } else { - if (av_mute_status & BIT(0)) { - hdmi_write(hdmi, HDMI_GC, av_mute_status & ~BIT(0)); - av_pkt_en = true; - } - } - - /* Enable AV Mute tranmission here */ - if (av_pkt_en) - hdmi_write(hdmi, HDMI_VBI_PKT_CTRL, - hdmi_read(hdmi, HDMI_VBI_PKT_CTRL) | (BIT(4) & BIT(5))); - - SDE_DEBUG("AVMUTE %s\n", set ? "set" : "cleared"); - - return 0; -} - diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index 34268aaedfc0..24d2320683e4 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -22,6 +22,71 @@ #include "sde_hdmi.h" #include "hdmi.h" +/* + * Add these register definitions to support the latest chipsets. These + * are derived from hdmi.xml.h and are going to be replaced by a chipset + * based mask approach. + */ +#define SDE_HDMI_ACTIVE_HSYNC_START__MASK 0x00001fff +static inline uint32_t SDE_HDMI_ACTIVE_HSYNC_START(uint32_t val) +{ + return ((val) << HDMI_ACTIVE_HSYNC_START__SHIFT) & + SDE_HDMI_ACTIVE_HSYNC_START__MASK; +} +#define SDE_HDMI_ACTIVE_HSYNC_END__MASK 0x1fff0000 +static inline uint32_t SDE_HDMI_ACTIVE_HSYNC_END(uint32_t val) +{ + return ((val) << HDMI_ACTIVE_HSYNC_END__SHIFT) & + SDE_HDMI_ACTIVE_HSYNC_END__MASK; +} + +#define SDE_HDMI_ACTIVE_VSYNC_START__MASK 0x00001fff +static inline uint32_t SDE_HDMI_ACTIVE_VSYNC_START(uint32_t val) +{ + return ((val) << HDMI_ACTIVE_VSYNC_START__SHIFT) & + SDE_HDMI_ACTIVE_VSYNC_START__MASK; +} +#define SDE_HDMI_ACTIVE_VSYNC_END__MASK 0x1fff0000 +static inline uint32_t SDE_HDMI_ACTIVE_VSYNC_END(uint32_t val) +{ + return ((val) << HDMI_ACTIVE_VSYNC_END__SHIFT) & + SDE_HDMI_ACTIVE_VSYNC_END__MASK; +} + +#define SDE_HDMI_VSYNC_ACTIVE_F2_START__MASK 0x00001fff +static inline uint32_t SDE_HDMI_VSYNC_ACTIVE_F2_START(uint32_t val) +{ + return ((val) << HDMI_VSYNC_ACTIVE_F2_START__SHIFT) & + SDE_HDMI_VSYNC_ACTIVE_F2_START__MASK; +} +#define SDE_HDMI_VSYNC_ACTIVE_F2_END__MASK 0x1fff0000 +static inline uint32_t SDE_HDMI_VSYNC_ACTIVE_F2_END(uint32_t val) +{ + return ((val) << HDMI_VSYNC_ACTIVE_F2_END__SHIFT) & + SDE_HDMI_VSYNC_ACTIVE_F2_END__MASK; +} + +#define SDE_HDMI_TOTAL_H_TOTAL__MASK 0x00001fff +static inline uint32_t SDE_HDMI_TOTAL_H_TOTAL(uint32_t val) +{ + return ((val) << HDMI_TOTAL_H_TOTAL__SHIFT) & + SDE_HDMI_TOTAL_H_TOTAL__MASK; +} + +#define SDE_HDMI_TOTAL_V_TOTAL__MASK 0x1fff0000 +static inline uint32_t SDE_HDMI_TOTAL_V_TOTAL(uint32_t val) +{ + return ((val) << HDMI_TOTAL_V_TOTAL__SHIFT) & + SDE_HDMI_TOTAL_V_TOTAL__MASK; +} + +#define SDE_HDMI_VSYNC_TOTAL_F2_V_TOTAL__MASK 0x00001fff +static inline uint32_t SDE_HDMI_VSYNC_TOTAL_F2_V_TOTAL(uint32_t val) +{ + return ((val) << HDMI_VSYNC_TOTAL_F2_V_TOTAL__SHIFT) & + SDE_HDMI_VSYNC_TOTAL_F2_V_TOTAL__MASK; +} + struct sde_hdmi_bridge { struct drm_bridge base; struct hdmi *hdmi; @@ -32,8 +97,7 @@ struct sde_hdmi_bridge { #define HDMI_TX_SCRAMBLER_MIN_TX_VERSION 0x04 #define HDMI_TX_SCRAMBLER_THRESHOLD_RATE_KHZ 340000 #define HDMI_TX_SCRAMBLER_TIMEOUT_MSEC 200 -/* default hsyncs for 4k@60 for 200ms */ -#define HDMI_DEFAULT_TIMEOUT_HSYNC 28571 + /* for AVI program */ #define HDMI_AVI_INFOFRAME_BUFFER_SIZE \ @@ -177,39 +241,22 @@ static int _sde_hdmi_bridge_scrambler_ddc_check_status(struct hdmi *hdmi) return rc; } -static void _sde_hdmi_bridge_scrambler_ddc_reset(struct hdmi *hdmi) -{ - u32 reg_val; - - /* clear ack and disable interrupts */ - reg_val = BIT(14) | BIT(9) | BIT(5) | BIT(1); - hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL2, reg_val); - - /* Reset DDC timers */ - reg_val = BIT(0) | hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL); - hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val); - - reg_val = hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL); - reg_val &= ~BIT(0); - hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val); -} - -static void _sde_hdmi_bridge_scrambler_ddc_disable(struct hdmi *hdmi) -{ - u32 reg_val; - - _sde_hdmi_bridge_scrambler_ddc_reset(hdmi); - /* Disable HW DDC access to RxStatus register */ - reg_val = hdmi_read(hdmi, REG_HDMI_HW_DDC_CTRL); - reg_val &= ~(BIT(8) | BIT(9)); - hdmi_write(hdmi, REG_HDMI_HW_DDC_CTRL, reg_val); -} - static int _sde_hdmi_bridge_scrambler_status_timer_setup(struct hdmi *hdmi, u32 timeout_hsync) { u32 reg_val; int rc; + struct sde_connector *c_conn; + struct drm_connector *connector = NULL; + struct sde_hdmi *display; + + if (!hdmi) { + SDE_ERROR("invalid input\n"); + return -EINVAL; + } + connector = hdmi->connector; + c_conn = to_sde_connector(hdmi->connector); + display = (struct sde_hdmi *)c_conn->display; _sde_hdmi_bridge_ddc_clear_irq(hdmi, "scrambler"); @@ -243,7 +290,7 @@ static int _sde_hdmi_bridge_scrambler_status_timer_setup(struct hdmi *hdmi, if (rc) SDE_ERROR("scrambling ddc error %d\n", rc); - _sde_hdmi_bridge_scrambler_ddc_disable(hdmi); + _sde_hdmi_scrambler_ddc_disable((void *)display); return rc; } @@ -269,20 +316,6 @@ static int _sde_hdmi_bridge_setup_ddc_timers(struct hdmi *hdmi, return 0; } -static inline int _sde_hdmi_bridge_get_timeout_in_hysnc( - struct drm_display_mode *mode, u32 timeout_ms) -{ - /* - * pixel clock = h_total * v_total * fps - * 1 sec = pixel clock number of pixels are transmitted. - * time taken by one line (h_total) = 1s / (v_total * fps). - * lines for give time = (time_ms * 1000) / (1000000 / (v_total * fps)) - * = (time_ms * clock) / h_total - */ - - return (timeout_ms * mode->clock / mode->htotal); -} - static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi, struct drm_display_mode *mode) { @@ -291,14 +324,17 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi, u32 reg_val = 0; u32 tmds_clock_ratio = 0; bool scrambler_on = false; - + struct sde_connector *c_conn; struct drm_connector *connector = NULL; + struct sde_hdmi *display; if (!hdmi || !mode) { SDE_ERROR("invalid input\n"); return -EINVAL; } connector = hdmi->connector; + c_conn = to_sde_connector(hdmi->connector); + display = (struct sde_hdmi *)c_conn->display; /* Read HDMI version */ reg_val = hdmi_read(hdmi, REG_HDMI_VERSION); @@ -328,7 +364,6 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi, } reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); - reg_val |= BIT(31); /* Enable Update DATAPATH_MODE */ reg_val |= BIT(28); /* Set SCRAMBLER_EN bit */ hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); @@ -345,9 +380,10 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi, * status bit on the sink. Sink should set this bit * with in 200ms after scrambler is enabled. */ - timeout_hsync = _sde_hdmi_bridge_get_timeout_in_hysnc( - mode, + timeout_hsync = _sde_hdmi_get_timeout_in_hysnc( + (void *)display, HDMI_TX_SCRAMBLER_TIMEOUT_MSEC); + if (timeout_hsync <= 0) { SDE_ERROR("err in timeout hsync calc\n"); timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC; @@ -360,7 +396,6 @@ static int _sde_hdmi_bridge_setup_scrambler(struct hdmi *hdmi, } else { sde_hdmi_scdc_write(hdmi, HDMI_TX_SCDC_SCRAMBLING_ENABLE, 0x0); reg_val = hdmi_read(hdmi, REG_HDMI_CTRL); - reg_val &= ~BIT(31); /* Disable Update DATAPATH_MODE */ reg_val &= ~BIT(28); /* Unset SCRAMBLER_EN bit */ hdmi_write(hdmi, REG_HDMI_CTRL, reg_val); } @@ -398,12 +433,77 @@ static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge) mutex_unlock(&display->display_lock); } +static void sde_hdmi_update_hdcp_info(struct drm_connector *connector) +{ + void *fd = NULL; + struct sde_hdcp_ops *ops = NULL; + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + + if (!display) { + DEV_ERR("%s: invalid input\n", __func__); + return; + } + + /* check first if hdcp2p2 is supported */ + fd = display->hdcp_feat_data[SDE_HDCP_2P2]; + if (fd) + ops = sde_hdmi_hdcp2p2_start(fd); + + /* If ops is true, sink supports hdcp */ + if (ops) + display->sink_hdcp22_support = true; + + if (ops && ops->feature_supported) + display->hdcp22_present = ops->feature_supported(fd); + else + display->hdcp22_present = false; + + /* if hdcp22_present is true, src supports hdcp 2p2 */ + if (display->hdcp22_present) + display->src_hdcp22_support = true; + + if (!display->hdcp22_present) { + if (display->hdcp1_use_sw_keys) { + display->hdcp14_present = + hdcp1_check_if_supported_load_app(); + } + if (display->hdcp14_present) { + fd = display->hdcp_feat_data[SDE_HDCP_1x]; + if (fd) + ops = sde_hdcp_1x_start(fd); + } + } + + /* update internal data about hdcp */ + display->hdcp_data = fd; + display->hdcp_ops = ops; +} + static void _sde_hdmi_bridge_enable(struct drm_bridge *bridge) { + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + + /* need to update hdcp info here to ensure right HDCP support*/ + sde_hdmi_update_hdcp_info(hdmi->connector); + + /* start HDCP authentication */ + sde_hdmi_start_hdcp(hdmi->connector); } static void _sde_hdmi_bridge_disable(struct drm_bridge *bridge) { + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + struct sde_connector *c_conn = to_sde_connector(hdmi->connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; + + mutex_lock(&display->display_lock); + + display->pll_update_enable = false; + + mutex_unlock(&display->display_lock); } static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) @@ -416,8 +516,8 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge) sde_hdmi_notify_clients(display, display->connected); - if (hdmi->hdcp_ctrl && hdmi->is_hdcp_supported) - hdmi_hdcp_ctrl_off(hdmi->hdcp_ctrl); + if (sde_hdmi_tx_is_hdcp_enabled(display)) + sde_hdmi_hdcp_off(display); sde_hdmi_audio_off(hdmi); @@ -584,28 +684,28 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge, mode->htotal, mode->vtotal, hstart, hend, vstart, vend); hdmi_write(hdmi, REG_HDMI_TOTAL, - HDMI_TOTAL_H_TOTAL(mode->htotal - 1) | - HDMI_TOTAL_V_TOTAL(mode->vtotal - 1)); + SDE_HDMI_TOTAL_H_TOTAL(mode->htotal - 1) | + SDE_HDMI_TOTAL_V_TOTAL(mode->vtotal - 1)); hdmi_write(hdmi, REG_HDMI_ACTIVE_HSYNC, - HDMI_ACTIVE_HSYNC_START(hstart) | - HDMI_ACTIVE_HSYNC_END(hend)); + SDE_HDMI_ACTIVE_HSYNC_START(hstart) | + SDE_HDMI_ACTIVE_HSYNC_END(hend)); hdmi_write(hdmi, REG_HDMI_ACTIVE_VSYNC, - HDMI_ACTIVE_VSYNC_START(vstart) | - HDMI_ACTIVE_VSYNC_END(vend)); + SDE_HDMI_ACTIVE_VSYNC_START(vstart) | + SDE_HDMI_ACTIVE_VSYNC_END(vend)); if (mode->flags & DRM_MODE_FLAG_INTERLACE) { hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2, - HDMI_VSYNC_TOTAL_F2_V_TOTAL(mode->vtotal)); + SDE_HDMI_VSYNC_TOTAL_F2_V_TOTAL(mode->vtotal)); hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2, - HDMI_VSYNC_ACTIVE_F2_START(vstart + 1) | - HDMI_VSYNC_ACTIVE_F2_END(vend + 1)); + SDE_HDMI_VSYNC_ACTIVE_F2_START(vstart + 1) | + SDE_HDMI_VSYNC_ACTIVE_F2_END(vend + 1)); } else { hdmi_write(hdmi, REG_HDMI_VSYNC_TOTAL_F2, - HDMI_VSYNC_TOTAL_F2_V_TOTAL(0)); + SDE_HDMI_VSYNC_TOTAL_F2_V_TOTAL(0)); hdmi_write(hdmi, REG_HDMI_VSYNC_ACTIVE_F2, - HDMI_VSYNC_ACTIVE_F2_START(0) | - HDMI_VSYNC_ACTIVE_F2_END(0)); + SDE_HDMI_VSYNC_ACTIVE_F2_START(0) | + SDE_HDMI_VSYNC_ACTIVE_F2_END(0)); } frame_ctrl = 0; @@ -631,9 +731,9 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge, _sde_hdmi_bridge_set_spd_infoframe(hdmi, mode); DRM_DEBUG("hdmi setup info frame\n"); } - _sde_hdmi_bridge_setup_scrambler(hdmi, mode); _sde_hdmi_save_mode(hdmi, mode); + _sde_hdmi_bridge_setup_scrambler(hdmi, mode); } static const struct drm_bridge_funcs _sde_hdmi_bridge_funcs = { diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c new file mode 100644 index 000000000000..1e673440f399 --- /dev/null +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_hdcp2p2.c @@ -0,0 +1,994 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/types.h> +#include <linux/kthread.h> + +#include <linux/hdcp_qseecom.h> +#include "sde_hdcp.h" +#include "video/msm_hdmi_hdcp_mgr.h" +#include "sde_hdmi_util.h" + +/* + * Defined addresses and offsets of standard HDCP 2.2 sink registers + * for DDC, as defined in HDCP 2.2 spec section 2.14 table 2.7 + */ +#define HDCP_SINK_DDC_SLAVE_ADDR 0x74 /* Sink DDC slave address */ +#define HDCP_SINK_DDC_HDCP2_VERSION 0x50 /* Does sink support HDCP2.2 */ +#define HDCP_SINK_DDC_HDCP2_WRITE_MESSAGE 0x60 /* HDCP Tx writes here */ +#define HDCP_SINK_DDC_HDCP2_RXSTATUS 0x70 /* RxStatus, 2 bytes */ +#define HDCP_SINK_DDC_HDCP2_READ_MESSAGE 0x80 /* HDCP Tx reads here */ + +#define HDCP2P2_DEFAULT_TIMEOUT 500 + +/* + * HDCP 2.2 encryption requires the data encryption block that is present in + * HDMI controller version 4.0.0 and above + */ +#define MIN_HDMI_TX_MAJOR_VERSION 4 + +enum sde_hdmi_hdcp2p2_sink_status { + SINK_DISCONNECTED, + SINK_CONNECTED +}; + +enum sde_hdmi_auth_status { + HDMI_HDCP_AUTH_STATUS_FAILURE, + HDMI_HDCP_AUTH_STATUS_SUCCESS +}; + +struct sde_hdmi_hdcp2p2_ctrl { + atomic_t auth_state; + enum sde_hdmi_hdcp2p2_sink_status sink_status; /* Is sink connected */ + struct sde_hdcp_init_data init_data; /* Feature data from HDMI drv */ + struct mutex mutex; /* mutex to protect access to ctrl */ + struct mutex msg_lock; /* mutex to protect access to msg buffer */ + struct mutex wakeup_mutex; /* mutex to protect access to wakeup call*/ + struct sde_hdcp_ops *ops; + void *lib_ctx; /* Handle to HDCP 2.2 Trustzone library */ + struct hdcp_txmtr_ops *lib; /* Ops for driver to call into TZ */ + + enum hdmi_hdcp_wakeup_cmd wakeup_cmd; + enum sde_hdmi_auth_status auth_status; + char *send_msg_buf; + uint32_t send_msg_len; + uint32_t timeout; + uint32_t timeout_left; + + struct task_struct *thread; + struct kthread_worker worker; + struct kthread_work status; + struct kthread_work auth; + struct kthread_work send_msg; + struct kthread_work recv_msg; + struct kthread_work link; + struct kthread_work poll; +}; + +static int sde_hdmi_hdcp2p2_auth(struct sde_hdmi_hdcp2p2_ctrl *ctrl); +static void sde_hdmi_hdcp2p2_send_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl); +static void sde_hdmi_hdcp2p2_recv_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl); +static void sde_hdmi_hdcp2p2_auth_status(struct sde_hdmi_hdcp2p2_ctrl *ctrl); +static int sde_hdmi_hdcp2p2_link_check(struct sde_hdmi_hdcp2p2_ctrl *ctrl); + +static bool sde_hdcp2p2_is_valid_state(struct sde_hdmi_hdcp2p2_ctrl *ctrl) +{ + if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_AUTHENTICATE) + return true; + + if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE) + return true; + + return false; +} + +static int sde_hdmi_hdcp2p2_copy_buf(struct sde_hdmi_hdcp2p2_ctrl *ctrl, + struct hdmi_hdcp_wakeup_data *data) +{ + mutex_lock(&ctrl->msg_lock); + + if (!data->send_msg_len) { + mutex_unlock(&ctrl->msg_lock); + return 0; + } + + ctrl->send_msg_len = data->send_msg_len; + + kzfree(ctrl->send_msg_buf); + + ctrl->send_msg_buf = kzalloc(data->send_msg_len, GFP_KERNEL); + + if (!ctrl->send_msg_buf) { + mutex_unlock(&ctrl->msg_lock); + return -ENOMEM; + } + + memcpy(ctrl->send_msg_buf, data->send_msg_buf, ctrl->send_msg_len); + + mutex_unlock(&ctrl->msg_lock); + + return 0; +} + +static int sde_hdmi_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data) +{ + struct sde_hdmi_hdcp2p2_ctrl *ctrl; + + if (!data) { + SDE_ERROR("invalid input\n"); + return -EINVAL; + } + + ctrl = data->context; + if (!ctrl) { + SDE_ERROR("invalid ctrl\n"); + return -EINVAL; + } + + mutex_lock(&ctrl->wakeup_mutex); + + SDE_HDCP_DEBUG("cmd: %s, timeout %dms\n", + hdmi_hdcp_cmd_to_str(data->cmd), + data->timeout); + + ctrl->wakeup_cmd = data->cmd; + + if (data->timeout) + ctrl->timeout = data->timeout * 2; + else + ctrl->timeout = HDCP2P2_DEFAULT_TIMEOUT; + + if (!sde_hdcp2p2_is_valid_state(ctrl)) { + SDE_ERROR("invalid state\n"); + goto exit; + } + + if (sde_hdmi_hdcp2p2_copy_buf(ctrl, data)) + goto exit; + + if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS) + ctrl->auth_status = HDMI_HDCP_AUTH_STATUS_SUCCESS; + else if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_STATUS_FAILED) + ctrl->auth_status = HDMI_HDCP_AUTH_STATUS_FAILURE; + + switch (ctrl->wakeup_cmd) { + case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE: + queue_kthread_work(&ctrl->worker, &ctrl->send_msg); + break; + case HDMI_HDCP_WKUP_CMD_RECV_MESSAGE: + queue_kthread_work(&ctrl->worker, &ctrl->recv_msg); + break; + case HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS: + case HDMI_HDCP_WKUP_CMD_STATUS_FAILED: + queue_kthread_work(&ctrl->worker, &ctrl->status); + break; + case HDMI_HDCP_WKUP_CMD_LINK_POLL: + queue_kthread_work(&ctrl->worker, &ctrl->poll); + break; + case HDMI_HDCP_WKUP_CMD_AUTHENTICATE: + queue_kthread_work(&ctrl->worker, &ctrl->auth); + break; + default: + SDE_ERROR("invalid wakeup command %d\n", ctrl->wakeup_cmd); + } +exit: + mutex_unlock(&ctrl->wakeup_mutex); + return 0; +} + +static int sde_hdmi_hdcp2p2_wakeup_lib(struct sde_hdmi_hdcp2p2_ctrl *ctrl, + struct hdcp_lib_wakeup_data *data) +{ + int rc = 0; + + if (ctrl && ctrl->lib && ctrl->lib->wakeup && + data && (data->cmd != HDCP_LIB_WKUP_CMD_INVALID)) { + rc = ctrl->lib->wakeup(data); + if (rc) + SDE_ERROR("error sending %s to lib\n", + hdcp_lib_cmd_to_str(data->cmd)); + } + + return rc; +} + +static void sde_hdmi_hdcp2p2_reset(struct sde_hdmi_hdcp2p2_ctrl *ctrl) +{ + if (!ctrl) { + SDE_ERROR("invalid input\n"); + return; + } + + ctrl->sink_status = SINK_DISCONNECTED; + atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE); +} + +static void sde_hdmi_hdcp2p2_off(void *input) +{ + struct sde_hdmi_hdcp2p2_ctrl *ctrl; + struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_AUTHENTICATE}; + + ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input; + + if (!ctrl) { + SDE_ERROR("invalid input\n"); + return; + } + + sde_hdmi_hdcp2p2_reset(ctrl); + + flush_kthread_worker(&ctrl->worker); + + sde_hdmi_hdcp2p2_ddc_disable((void *)ctrl->init_data.cb_data); + + cdata.context = input; + sde_hdmi_hdcp2p2_wakeup(&cdata); +} + +static int sde_hdmi_hdcp2p2_authenticate(void *input) +{ + struct sde_hdmi_hdcp2p2_ctrl *ctrl = input; + struct hdmi_hdcp_wakeup_data cdata = {HDMI_HDCP_WKUP_CMD_AUTHENTICATE}; + u32 regval; + int rc = 0; + + /* Enable authentication success interrupt */ + regval = DSS_REG_R(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2); + regval |= BIT(1) | BIT(2); + + DSS_REG_W(ctrl->init_data.core_io, HDMI_HDCP_INT_CTRL2, regval); + + flush_kthread_worker(&ctrl->worker); + + ctrl->sink_status = SINK_CONNECTED; + atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATING); + + /* make sure ddc is idle before starting hdcp 2.2 authentication */ + _sde_hdmi_scrambler_ddc_disable((void *)ctrl->init_data.cb_data); + sde_hdmi_hdcp2p2_ddc_disable((void *)ctrl->init_data.cb_data); + + cdata.context = input; + sde_hdmi_hdcp2p2_wakeup(&cdata); + + return rc; +} + +static int sde_hdmi_hdcp2p2_reauthenticate(void *input) +{ + struct sde_hdmi_hdcp2p2_ctrl *ctrl; + + ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input; + + if (!ctrl) { + SDE_ERROR("invalid input\n"); + return -EINVAL; + } + + sde_hdmi_hdcp2p2_reset(ctrl); + + return sde_hdmi_hdcp2p2_authenticate(input); +} + +static void sde_hdmi_hdcp2p2_min_level_change(void *client_ctx, +int min_enc_lvl) +{ + struct sde_hdmi_hdcp2p2_ctrl *ctrl = + (struct sde_hdmi_hdcp2p2_ctrl *)client_ctx; + struct hdcp_lib_wakeup_data cdata = { + HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE}; + bool enc_notify = true; + enum sde_hdcp_states enc_lvl; + + if (!ctrl) { + SDE_ERROR("invalid input\n"); + return; + } + + switch (min_enc_lvl) { + case 0: + enc_lvl = HDCP_STATE_AUTH_ENC_NONE; + break; + case 1: + enc_lvl = HDCP_STATE_AUTH_ENC_1X; + break; + case 2: + enc_lvl = HDCP_STATE_AUTH_ENC_2P2; + break; + default: + enc_notify = false; + } + + SDE_HDCP_DEBUG("enc level changed %d\n", min_enc_lvl); + + cdata.context = ctrl->lib_ctx; + sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata); + + if (enc_notify && ctrl->init_data.notify_status) + ctrl->init_data.notify_status(ctrl->init_data.cb_data, enc_lvl); +} + +static void sde_hdmi_hdcp2p2_auth_failed(struct sde_hdmi_hdcp2p2_ctrl *ctrl) +{ + if (!ctrl) { + SDE_ERROR("invalid input\n"); + return; + } + + atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL); + + sde_hdmi_hdcp2p2_ddc_disable(ctrl->init_data.cb_data); + + /* notify hdmi tx about HDCP failure */ + ctrl->init_data.notify_status(ctrl->init_data.cb_data, + HDCP_STATE_AUTH_FAIL); +} + +static int sde_hdmi_hdcp2p2_ddc_rd_message(struct sde_hdmi_hdcp2p2_ctrl *ctrl, + u8 *buf, int size, u32 timeout) +{ + struct sde_hdmi_tx_ddc_data *ddc_data; + struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl; + + int rc; + + if (!ctrl) { + SDE_ERROR("invalid ctrl\n"); + return -EINVAL; + } + + ddc_ctrl = ctrl->init_data.ddc_ctrl; + ddc_data = &ddc_ctrl->ddc_data; + + if (!ddc_data) { + SDE_ERROR("invalid ddc data\n"); + return -EINVAL; + } + + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + SDE_ERROR("hdcp is off\n"); + return -EINVAL; + } + + memset(ddc_data, 0, sizeof(*ddc_data)); + ddc_data->dev_addr = HDCP_SINK_DDC_SLAVE_ADDR; + ddc_data->offset = HDCP_SINK_DDC_HDCP2_READ_MESSAGE; + ddc_data->data_buf = buf; + ddc_data->data_len = size; + ddc_data->request_len = size; + ddc_data->retry = 0; + ddc_data->hard_timeout = timeout; + ddc_data->what = "HDCP2ReadMessage"; + + rc = sde_hdmi_ddc_read(ctrl->init_data.cb_data); + if (rc) + SDE_ERROR("Cannot read HDCP message register\n"); + + ctrl->timeout_left = ddc_data->timeout_left; + + return rc; +} + +static int sde_hdmi_hdcp2p2_ddc_wt_message(struct sde_hdmi_hdcp2p2_ctrl *ctrl, + u8 *buf, size_t size) +{ + struct sde_hdmi_tx_ddc_data *ddc_data; + struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl; + + int rc; + + if (!ctrl) { + SDE_ERROR("invalid ctrl\n"); + return -EINVAL; + } + + ddc_ctrl = ctrl->init_data.ddc_ctrl; + ddc_data = &ddc_ctrl->ddc_data; + + if (!ddc_data) { + SDE_ERROR("invalid ddc data\n"); + return -EINVAL; + } + + memset(ddc_data, 0, sizeof(*ddc_data)); + ddc_data->dev_addr = HDCP_SINK_DDC_SLAVE_ADDR; + ddc_data->offset = HDCP_SINK_DDC_HDCP2_WRITE_MESSAGE; + ddc_data->data_buf = buf; + ddc_data->data_len = size; + ddc_data->hard_timeout = ctrl->timeout; + ddc_data->what = "HDCP2WriteMessage"; + + rc = sde_hdmi_ddc_write((void *)ctrl->init_data.cb_data); + if (rc) + SDE_ERROR("Cannot write HDCP message register\n"); + + ctrl->timeout_left = ddc_data->timeout_left; + + return rc; +} + +static int sde_hdmi_hdcp2p2_read_version(struct sde_hdmi_hdcp2p2_ctrl *ctrl, + u8 *hdcp2version) +{ + struct sde_hdmi_tx_ddc_data *ddc_data; + struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl; + int rc; + + if (!ctrl) { + SDE_ERROR("invalid ctrl\n"); + return -EINVAL; + } + + ddc_ctrl = ctrl->init_data.ddc_ctrl; + ddc_data = &ddc_ctrl->ddc_data; + + if (!ddc_data) { + SDE_ERROR("invalid ddc data\n"); + return -EINVAL; + } + memset(ddc_data, 0, sizeof(*ddc_data)); + ddc_data->dev_addr = HDCP_SINK_DDC_SLAVE_ADDR; + ddc_data->offset = HDCP_SINK_DDC_HDCP2_VERSION; + ddc_data->data_buf = hdcp2version; + ddc_data->data_len = 1; + ddc_data->request_len = 1; + ddc_data->retry = 1; + ddc_data->what = "HDCP2Version"; + + rc = sde_hdmi_ddc_read((void *)ctrl->init_data.cb_data); + if (rc) { + SDE_ERROR("Cannot read HDCP2Version register"); + return rc; + } + + SDE_HDCP_DEBUG("Read HDCP2Version as %u\n", *hdcp2version); + return rc; +} + +static bool sde_hdmi_hdcp2p2_feature_supported(void *input) +{ + struct sde_hdmi_hdcp2p2_ctrl *ctrl = input; + struct hdcp_txmtr_ops *lib = NULL; + bool supported = false; + + if (!ctrl) { + SDE_ERROR("invalid input\n"); + goto end; + } + + lib = ctrl->lib; + if (!lib) { + SDE_ERROR("invalid lib ops data\n"); + goto end; + } + + if (lib->feature_supported) { + supported = lib->feature_supported( + ctrl->lib_ctx); + } + +end: + return supported; +} + +static void sde_hdmi_hdcp2p2_send_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl) +{ + int rc = 0; + struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; + uint32_t msglen; + char *msg = NULL; + + if (!ctrl) { + SDE_ERROR("invalid input\n"); + rc = -EINVAL; + goto exit; + } + + cdata.context = ctrl->lib_ctx; + + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + SDE_ERROR("hdcp is off\n"); + goto exit; + } + + mutex_lock(&ctrl->msg_lock); + msglen = ctrl->send_msg_len; + + if (!msglen) { + mutex_unlock(&ctrl->msg_lock); + rc = -EINVAL; + goto exit; + } + + msg = kzalloc(msglen, GFP_KERNEL); + if (!msg) { + mutex_unlock(&ctrl->msg_lock); + rc = -ENOMEM; + goto exit; + } + + memcpy(msg, ctrl->send_msg_buf, msglen); + mutex_unlock(&ctrl->msg_lock); + + /* Forward the message to the sink */ + rc = sde_hdmi_hdcp2p2_ddc_wt_message(ctrl, + msg, (size_t)msglen); + if (rc) { + SDE_ERROR("Error sending msg to sink %d\n", rc); + cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED; + } else { + cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS; + cdata.timeout = ctrl->timeout_left; + } +exit: + kfree(msg); + + sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata); +} + +static void sde_hdmi_hdcp2p2_send_msg_work(struct kthread_work *work) +{ + struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct sde_hdmi_hdcp2p2_ctrl, send_msg); + + sde_hdmi_hdcp2p2_send_msg(ctrl); +} + +static void sde_hdmi_hdcp2p2_link_cb(void *data) +{ + struct sde_hdmi_hdcp2p2_ctrl *ctrl = data; + + if (!ctrl) { + SDE_HDCP_DEBUG("invalid input\n"); + return; + } + + if (atomic_read(&ctrl->auth_state) != HDCP_STATE_INACTIVE) + queue_kthread_work(&ctrl->worker, &ctrl->link); +} + +static void sde_hdmi_hdcp2p2_recv_msg(struct sde_hdmi_hdcp2p2_ctrl *ctrl) +{ + int timeout_hsync = 0, rc = 0; + char *recvd_msg_buf = NULL; + struct sde_hdmi_tx_hdcp2p2_ddc_data *ddc_data; + struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl; + struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; + + if (!ctrl) { + SDE_ERROR("invalid input\n"); + rc = -EINVAL; + goto exit; + } + + cdata.context = ctrl->lib_ctx; + + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + SDE_ERROR("hdcp is off\n"); + goto exit; + } + + ddc_ctrl = ctrl->init_data.ddc_ctrl; + if (!ddc_ctrl) { + pr_err("invalid ddc ctrl\n"); + rc = -EINVAL; + goto exit; + } + + ddc_data = &ddc_ctrl->sde_hdcp2p2_ddc_data; + memset(ddc_data, 0, sizeof(*ddc_data)); + + timeout_hsync = _sde_hdmi_get_timeout_in_hysnc( + (void *)ctrl->init_data.cb_data, ctrl->timeout); + + if (timeout_hsync <= 0) { + SDE_ERROR("err in timeout hsync calc\n"); + timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC; + } + + SDE_HDCP_DEBUG("timeout for rxstatus %dms, %d hsync\n", + ctrl->timeout, timeout_hsync); + + ddc_data->intr_mask = RXSTATUS_MESSAGE_SIZE | RXSTATUS_REAUTH_REQ; + ddc_data->timeout_ms = ctrl->timeout; + ddc_data->timeout_hsync = timeout_hsync; + ddc_data->periodic_timer_hsync = timeout_hsync / 20; + ddc_data->read_method = HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER; + ddc_data->wait = true; + + rc = sde_hdmi_hdcp2p2_read_rxstatus(ctrl->init_data.cb_data); + if (rc) { + SDE_ERROR("error reading rxstatus %d\n", rc); + goto exit; + } + + if (ddc_data->reauth_req) { + ddc_data->reauth_req = false; + + SDE_HDCP_DEBUG("reauth triggered by sink\n"); + rc = -EINVAL; + goto exit; + } + + ctrl->timeout_left = ddc_data->timeout_left; + + SDE_HDCP_DEBUG("timeout left after rxstatus %dms, msg size %d\n", + ctrl->timeout_left, ddc_data->message_size); + + if (!ddc_data->message_size) { + SDE_ERROR("recvd invalid message size\n"); + rc = -EINVAL; + goto exit; + } + + recvd_msg_buf = kzalloc(ddc_data->message_size, GFP_KERNEL); + if (!recvd_msg_buf) { + rc = -ENOMEM; + goto exit; + } + + rc = sde_hdmi_hdcp2p2_ddc_rd_message(ctrl, recvd_msg_buf, + ddc_data->message_size, ctrl->timeout_left); + if (rc) { + SDE_ERROR("error reading message %d\n", rc); + goto exit; + } + + cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS; + cdata.recvd_msg_buf = recvd_msg_buf; + cdata.recvd_msg_len = ddc_data->message_size; + cdata.timeout = ctrl->timeout_left; +exit: + if (rc == -ETIMEDOUT) + cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT; + else if (rc) + cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED; + + sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata); + kfree(recvd_msg_buf); +} + +static void sde_hdmi_hdcp2p2_recv_msg_work(struct kthread_work *work) +{ + struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct sde_hdmi_hdcp2p2_ctrl, recv_msg); + + sde_hdmi_hdcp2p2_recv_msg(ctrl); +} + +static int sde_hdmi_hdcp2p2_link_check(struct sde_hdmi_hdcp2p2_ctrl *ctrl) +{ + struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl; + struct sde_hdmi_tx_hdcp2p2_ddc_data *ddc_data; + int timeout_hsync; + int ret; + + ddc_ctrl = ctrl->init_data.ddc_ctrl; + + if (!ddc_ctrl) + return -EINVAL; + + sde_hdmi_ddc_config(ctrl->init_data.cb_data); + + ddc_data = &ddc_ctrl->sde_hdcp2p2_ddc_data; + + memset(ddc_data, 0, sizeof(*ddc_data)); + + timeout_hsync = _sde_hdmi_get_timeout_in_hysnc( + (void *)ctrl->init_data.cb_data, + jiffies_to_msecs(HZ / 2)); + + if (timeout_hsync <= 0) { + SDE_ERROR("err in timeout hsync calc\n"); + timeout_hsync = HDMI_DEFAULT_TIMEOUT_HSYNC; + } + SDE_HDCP_DEBUG("timeout for rxstatus %d hsyncs\n", timeout_hsync); + + ddc_data->intr_mask = RXSTATUS_READY | RXSTATUS_MESSAGE_SIZE | + RXSTATUS_REAUTH_REQ; + ddc_data->timeout_hsync = timeout_hsync; + ddc_data->periodic_timer_hsync = timeout_hsync; + ddc_data->read_method = HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER; + ddc_data->link_cb = sde_hdmi_hdcp2p2_link_cb; + ddc_data->link_data = ctrl; + + ret = sde_hdmi_hdcp2p2_read_rxstatus((void *)ctrl->init_data.cb_data); + return ret; +} + +static void sde_hdmi_hdcp2p2_poll_work(struct kthread_work *work) +{ + struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct sde_hdmi_hdcp2p2_ctrl, poll); + + sde_hdmi_hdcp2p2_link_check(ctrl); +} + +static void sde_hdmi_hdcp2p2_auth_status(struct sde_hdmi_hdcp2p2_ctrl *ctrl) +{ + if (!ctrl) { + SDE_ERROR("invalid input\n"); + return; + } + + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { + SDE_ERROR("hdcp is off\n"); + return; + } + + if (ctrl->auth_status == HDMI_HDCP_AUTH_STATUS_SUCCESS) { + ctrl->init_data.notify_status(ctrl->init_data.cb_data, + HDCP_STATE_AUTHENTICATED); + + atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATED); + } else { + sde_hdmi_hdcp2p2_auth_failed(ctrl); + } +} + +static void sde_hdmi_hdcp2p2_auth_status_work(struct kthread_work *work) +{ + struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct sde_hdmi_hdcp2p2_ctrl, status); + + sde_hdmi_hdcp2p2_auth_status(ctrl); +} + +static void sde_hdmi_hdcp2p2_link_work(struct kthread_work *work) +{ + int rc = 0; + struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct sde_hdmi_hdcp2p2_ctrl, link); + struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; + char *recvd_msg_buf = NULL; + struct sde_hdmi_tx_hdcp2p2_ddc_data *ddc_data; + struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl; + + if (!ctrl) { + SDE_ERROR("invalid input\n"); + return; + } + + cdata.context = ctrl->lib_ctx; + + ddc_ctrl = ctrl->init_data.ddc_ctrl; + if (!ddc_ctrl) { + rc = -EINVAL; + cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; + goto exit; + } + + ddc_data = &ddc_ctrl->sde_hdcp2p2_ddc_data; + + if (ddc_data->reauth_req) { + SDE_HDCP_DEBUG("reauth triggered by sink\n"); + + ddc_data->reauth_req = false; + rc = -ENOLINK; + cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; + goto exit; + } + + if (ddc_data->ready && ddc_data->message_size) { + SDE_HDCP_DEBUG("topology changed. rxstatus msg size %d\n", + ddc_data->message_size); + + ddc_data->ready = false; + + recvd_msg_buf = kzalloc(ddc_data->message_size, GFP_KERNEL); + if (!recvd_msg_buf) { + cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; + goto exit; + } + + rc = sde_hdmi_hdcp2p2_ddc_rd_message(ctrl, recvd_msg_buf, + ddc_data->message_size, HDCP2P2_DEFAULT_TIMEOUT); + if (rc) { + cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; + SDE_ERROR("error reading message %d\n", rc); + } else { + cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS; + cdata.recvd_msg_buf = recvd_msg_buf; + cdata.recvd_msg_len = ddc_data->message_size; + } + + ddc_data->message_size = 0; + } +exit: + sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata); + kfree(recvd_msg_buf); + + if (rc) { + sde_hdmi_hdcp2p2_auth_failed(ctrl); + return; + } +} + +static int sde_hdmi_hdcp2p2_auth(struct sde_hdmi_hdcp2p2_ctrl *ctrl) +{ + struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; + int rc = 0; + + if (!ctrl) { + SDE_ERROR("invalid input\n"); + return -EINVAL; + } + + cdata.context = ctrl->lib_ctx; + + if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING) + cdata.cmd = HDCP_LIB_WKUP_CMD_START; + else + cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; + + rc = sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata); + if (rc) + sde_hdmi_hdcp2p2_auth_failed(ctrl); + + return rc; +} + +static void sde_hdmi_hdcp2p2_auth_work(struct kthread_work *work) +{ + struct sde_hdmi_hdcp2p2_ctrl *ctrl = container_of(work, + struct sde_hdmi_hdcp2p2_ctrl, auth); + + sde_hdmi_hdcp2p2_auth(ctrl); +} + +void sde_hdmi_hdcp2p2_deinit(void *input) +{ + struct sde_hdmi_hdcp2p2_ctrl *ctrl; + struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; + + ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input; + + if (!ctrl) { + SDE_ERROR("invalid input\n"); + return; + } + + cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; + cdata.context = ctrl->lib_ctx; + sde_hdmi_hdcp2p2_wakeup_lib(ctrl, &cdata); + + kthread_stop(ctrl->thread); + + mutex_destroy(&ctrl->mutex); + mutex_destroy(&ctrl->msg_lock); + mutex_destroy(&ctrl->wakeup_mutex); + kfree(ctrl); +} + +void *sde_hdmi_hdcp2p2_init(struct sde_hdcp_init_data *init_data) +{ + int rc; + struct sde_hdmi_hdcp2p2_ctrl *ctrl; + static struct sde_hdcp_ops ops = { + .reauthenticate = sde_hdmi_hdcp2p2_reauthenticate, + .authenticate = sde_hdmi_hdcp2p2_authenticate, + .feature_supported = sde_hdmi_hdcp2p2_feature_supported, + .off = sde_hdmi_hdcp2p2_off + }; + + static struct hdcp_client_ops client_ops = { + .wakeup = sde_hdmi_hdcp2p2_wakeup, + .notify_lvl_change = sde_hdmi_hdcp2p2_min_level_change, + }; + + static struct hdcp_txmtr_ops txmtr_ops; + struct hdcp_register_data register_data; + + SDE_HDCP_DEBUG("HDCP2P2 feature initialization\n"); + + if (!init_data || !init_data->core_io || !init_data->mutex || + !init_data->ddc_ctrl || !init_data->notify_status || + !init_data->workq || !init_data->cb_data) { + SDE_ERROR("invalid input\n"); + return ERR_PTR(-EINVAL); + } + + if (init_data->hdmi_tx_ver < MIN_HDMI_TX_MAJOR_VERSION) { + SDE_ERROR("HDMI Tx does not support HDCP 2.2\n"); + return ERR_PTR(-ENODEV); + } + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return ERR_PTR(-ENOMEM); + + ctrl->init_data = *init_data; + ctrl->lib = &txmtr_ops; + + ctrl->sink_status = SINK_DISCONNECTED; + + atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE); + + ctrl->ops = &ops; + mutex_init(&ctrl->mutex); + mutex_init(&ctrl->msg_lock); + mutex_init(&ctrl->wakeup_mutex); + + register_data.hdcp_ctx = &ctrl->lib_ctx; + register_data.client_ops = &client_ops; + register_data.txmtr_ops = &txmtr_ops; + register_data.device_type = HDCP_TXMTR_HDMI; + register_data.client_ctx = ctrl; + + rc = hdcp_library_register(®ister_data); + if (rc) { + SDE_ERROR("Unable to register with HDCP 2.2 library\n"); + goto error; + } + + init_kthread_worker(&ctrl->worker); + + init_kthread_work(&ctrl->auth, sde_hdmi_hdcp2p2_auth_work); + init_kthread_work(&ctrl->send_msg, sde_hdmi_hdcp2p2_send_msg_work); + init_kthread_work(&ctrl->recv_msg, sde_hdmi_hdcp2p2_recv_msg_work); + init_kthread_work(&ctrl->status, sde_hdmi_hdcp2p2_auth_status_work); + init_kthread_work(&ctrl->link, sde_hdmi_hdcp2p2_link_work); + init_kthread_work(&ctrl->poll, sde_hdmi_hdcp2p2_poll_work); + + ctrl->thread = kthread_run(kthread_worker_fn, + &ctrl->worker, "hdmi_hdcp2p2"); + + if (IS_ERR(ctrl->thread)) { + SDE_ERROR("unable to start hdcp2p2 thread\n"); + rc = PTR_ERR(ctrl->thread); + ctrl->thread = NULL; + goto error; + } + + return ctrl; +error: + kfree(ctrl); + return ERR_PTR(rc); +} + +static bool sde_hdmi_hdcp2p2_supported(struct sde_hdmi_hdcp2p2_ctrl *ctrl) +{ + u8 hdcp2version = 0; + int rc = sde_hdmi_hdcp2p2_read_version(ctrl, &hdcp2version); + + if (rc) + goto error; + + if (hdcp2version & BIT(2)) { + SDE_HDCP_DEBUG("Sink is HDCP 2.2 capable\n"); + return true; + } + +error: + SDE_HDCP_DEBUG("Sink is not HDCP 2.2 capable\n"); + return false; +} + +struct sde_hdcp_ops *sde_hdmi_hdcp2p2_start(void *input) +{ + struct sde_hdmi_hdcp2p2_ctrl *ctrl; + + ctrl = (struct sde_hdmi_hdcp2p2_ctrl *)input; + + SDE_HDCP_DEBUG("Checking sink capability\n"); + if (sde_hdmi_hdcp2p2_supported(ctrl)) + return ctrl->ops; + else + return NULL; + +} + diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c new file mode 100644 index 000000000000..a7887d2c84b0 --- /dev/null +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c @@ -0,0 +1,827 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/iopoll.h> +#include <linux/types.h> +#include <linux/switch.h> +#include <linux/gcd.h> + +#include "drm_edid.h" +#include "sde_kms.h" +#include "sde_hdmi.h" +#include "sde_hdmi_regs.h" +#include "hdmi.h" + +#define HDMI_SEC_TO_MS 1000 +#define HDMI_MS_TO_US 1000 +#define HDMI_SEC_TO_US (HDMI_SEC_TO_MS * HDMI_MS_TO_US) +#define HDMI_KHZ_TO_HZ 1000 +#define HDMI_BUSY_WAIT_DELAY_US 100 + +static void sde_hdmi_hdcp2p2_ddc_clear_status(struct sde_hdmi *display) +{ + u32 reg_val; + struct hdmi *hdmi; + + if (!display) { + pr_err("invalid ddc ctrl\n"); + return; + } + hdmi = display->ctrl.ctrl; + /* check for errors and clear status */ + reg_val = hdmi_read(hdmi, HDMI_HDCP2P2_DDC_STATUS); + + if (reg_val & BIT(4)) { + pr_debug("ddc aborted\n"); + reg_val |= BIT(5); + } + + if (reg_val & BIT(8)) { + pr_debug("timed out\n"); + reg_val |= BIT(9); + } + + if (reg_val & BIT(12)) { + pr_debug("NACK0\n"); + reg_val |= BIT(13); + } + + if (reg_val & BIT(14)) { + pr_debug("NACK1\n"); + reg_val |= BIT(15); + } + + hdmi_write(hdmi, HDMI_HDCP2P2_DDC_STATUS, reg_val); +} + +/** + * sde_hdmi_dump_regs - utility to dump HDMI regs + * @hdmi_display: Pointer to private display handle + * Return : void + */ + +void sde_hdmi_dump_regs(void *hdmi_display) +{ + struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display; + struct hdmi *hdmi; + int i; + u32 addr_off = 0; + u32 len = 0; + + if (!display) { + pr_err("invalid input\n"); + return; + } + + hdmi = display->ctrl.ctrl; + + if (!hdmi) { + pr_err("invalid input\n"); + return; + } + + if (!hdmi->power_on || !display->connected) { + SDE_ERROR("HDMI display is not ready\n"); + return; + } + + len = hdmi->mmio_len; + + if (len % 16) + len += 16; + len /= 16; + + pr_info("HDMI CORE regs\n"); + for (i = 0; i < len; i++) { + u32 x0, x4, x8, xc; + + x0 = hdmi_read(hdmi, addr_off+0x0); + x4 = hdmi_read(hdmi, addr_off+0x4); + x8 = hdmi_read(hdmi, addr_off+0x8); + xc = hdmi_read(hdmi, addr_off+0xc); + + pr_info("%08x : %08x %08x %08x %08x\n", addr_off, x0, x4, x8, + xc); + + addr_off += 16; + } +} + +int sde_hdmi_ddc_hdcp2p2_isr(void *hdmi_display) +{ + struct sde_hdmi_tx_hdcp2p2_ddc_data *data; + u32 intr0, intr2, intr5; + u32 msg_size; + int rc = 0; + struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl; + struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display; + struct hdmi *hdmi; + + ddc_ctrl = &display->ddc_ctrl; + data = &ddc_ctrl->sde_hdcp2p2_ddc_data; + hdmi = display->ctrl.ctrl; + + if (!hdmi) { + pr_err("invalid input\n"); + return -EINVAL; + } + + intr0 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL0); + intr2 = hdmi_read(hdmi, HDMI_HDCP_INT_CTRL2); + intr5 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL5); + + pr_debug("intr0: 0x%x, intr2: 0x%x, intr5: 0x%x\n", + intr0, intr2, intr5); + + /* check if encryption is enabled */ + if (intr2 & BIT(0)) { + /* + * ack encryption ready interrupt. + * disable encryption ready interrupt. + * enable encryption not ready interrupt. + */ + intr2 &= ~BIT(2); + intr2 |= BIT(1) | BIT(6); + + pr_info("HDCP 2.2 Encryption enabled\n"); + data->encryption_ready = true; + } + + /* check if encryption is disabled */ + if (intr2 & BIT(4)) { + /* + * ack encryption not ready interrupt. + * disable encryption not ready interrupt. + * enable encryption ready interrupt. + */ + intr2 &= ~BIT(6); + intr2 |= BIT(5) | BIT(2); + + pr_info("HDCP 2.2 Encryption disabled\n"); + data->encryption_ready = false; + } + + hdmi_write(hdmi, HDMI_HDCP_INT_CTRL2, intr2); + + /* get the message size bits 29:20 */ + msg_size = (intr0 & (0x3FF << 20)) >> 20; + + if (msg_size) { + /* ack and disable message size interrupt */ + intr0 |= BIT(30); + intr0 &= ~BIT(31); + + data->message_size = msg_size; + } + + /* check and disable ready interrupt */ + if (intr0 & BIT(16)) { + /* ack ready/not ready interrupt */ + intr0 |= BIT(17); + intr0 &= ~BIT(18); + pr_debug("got ready interrupt\n"); + data->ready = true; + } + + /* check for reauth req interrupt */ + if (intr0 & BIT(12)) { + /* ack and disable reauth req interrupt */ + intr0 |= BIT(13); + intr0 &= ~BIT(14); + pr_err("got reauth interrupt\n"); + data->reauth_req = true; + } + + /* check for ddc fail interrupt */ + if (intr0 & BIT(8)) { + /* ack ddc fail interrupt */ + intr0 |= BIT(9); + pr_err("got ddc fail interrupt\n"); + data->ddc_max_retries_fail = true; + } + + /* check for ddc done interrupt */ + if (intr0 & BIT(4)) { + /* ack ddc done interrupt */ + intr0 |= BIT(5); + pr_debug("got ddc done interrupt\n"); + data->ddc_done = true; + } + + /* check for ddc read req interrupt */ + if (intr0 & BIT(0)) { + /* ack read req interrupt */ + intr0 |= BIT(1); + + data->ddc_read_req = true; + } + + hdmi_write(hdmi, HDMI_DDC_INT_CTRL0, intr0); + + if (intr5 & BIT(0)) { + pr_err("RXSTATUS_DDC_REQ_TIMEOUT\n"); + + /* ack and disable timeout interrupt */ + intr5 |= BIT(1); + intr5 &= ~BIT(2); + + data->ddc_timeout = true; + } + hdmi_write(hdmi, HDMI_DDC_INT_CTRL5, intr5); + + if (data->message_size || data->ready || data->reauth_req) { + if (data->wait) { + complete(&ddc_ctrl->rx_status_done); + } else if (data->link_cb && data->link_data) { + data->link_cb(data->link_data); + } else { + pr_err("new msg/reauth not handled\n"); + rc = -EINVAL; + } + } + + sde_hdmi_hdcp2p2_ddc_clear_status(display); + + return rc; +} + +int sde_hdmi_ddc_scrambling_isr(void *hdmi_display) +{ + + bool scrambler_timer_off = false; + u32 intr2, intr5; + struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display; + struct hdmi *hdmi; + + + hdmi = display->ctrl.ctrl; + + if (!hdmi) { + pr_err("invalid input\n"); + return -EINVAL; + } + + intr2 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL2); + intr5 = hdmi_read(hdmi, HDMI_DDC_INT_CTRL5); + + pr_debug("intr2: 0x%x, intr5: 0x%x\n", intr2, intr5); + + if (intr2 & BIT(12)) { + pr_err("SCRAMBLER_STATUS_NOT\n"); + + intr2 |= BIT(14); + scrambler_timer_off = true; + } + + if (intr2 & BIT(8)) { + pr_err("SCRAMBLER_STATUS_DDC_FAILED\n"); + + intr2 |= BIT(9); + + scrambler_timer_off = true; + } + hdmi_write(hdmi, HDMI_DDC_INT_CTRL2, intr2); + + if (intr5 & BIT(8)) { + pr_err("SCRAMBLER_STATUS_DDC_REQ_TIMEOUT\n"); + intr5 |= BIT(9); + intr5 &= ~BIT(10); + scrambler_timer_off = true; + } + hdmi_write(hdmi, HDMI_DDC_INT_CTRL5, intr5); + + if (scrambler_timer_off) + _sde_hdmi_scrambler_ddc_disable((void *)display); + + return 0; +} + +static int sde_hdmi_ddc_read_retry(struct sde_hdmi *display) +{ + int status; + int busy_wait_us; + struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl; + struct sde_hdmi_tx_ddc_data *ddc_data; + struct hdmi *hdmi; + + if (!display) { + SDE_ERROR("invalid input\n"); + return -EINVAL; + } + + hdmi = display->ctrl.ctrl; + ddc_ctrl = &display->ddc_ctrl; + ddc_data = &ddc_ctrl->ddc_data; + + if (!ddc_data) { + SDE_ERROR("invalid input\n"); + return -EINVAL; + } + + if (!ddc_data->data_buf) { + status = -EINVAL; + SDE_ERROR("%s: invalid buf\n", ddc_data->what); + goto error; + } + + if (ddc_data->retry < 0) { + SDE_ERROR("invalid no. of retries %d\n", ddc_data->retry); + status = -EINVAL; + goto error; + } + + do { + if (ddc_data->hard_timeout) { + HDMI_UTIL_DEBUG("using hard_timeout %dms\n", + ddc_data->hard_timeout); + + busy_wait_us = ddc_data->hard_timeout * HDMI_MS_TO_US; + hdmi->use_hard_timeout = true; + hdmi->busy_wait_us = busy_wait_us; + } + + /* Calling upstream ddc read method */ + status = hdmi_ddc_read(hdmi, ddc_data->dev_addr, + ddc_data->offset, + ddc_data->data_buf, ddc_data->request_len, + false); + + if (ddc_data->hard_timeout) + ddc_data->timeout_left = hdmi->timeout_count; + + + if (ddc_data->hard_timeout && !hdmi->timeout_count) { + HDMI_UTIL_DEBUG("%s: timedout\n", ddc_data->what); + status = -ETIMEDOUT; + } + + } while (status && ddc_data->retry--); + + if (status) { + HDMI_UTIL_ERROR("%s: failed status = %d\n", + ddc_data->what, status); + goto error; + } + + HDMI_UTIL_DEBUG("%s: success\n", ddc_data->what); + +error: + return status; +} /* sde_hdmi_ddc_read_retry */ + +int sde_hdmi_ddc_read(void *cb_data) +{ + int rc = 0; + int retry; + struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl; + struct sde_hdmi_tx_ddc_data *ddc_data; + struct sde_hdmi *display = (struct sde_hdmi *)cb_data; + + if (!display) { + SDE_ERROR("invalid ddc ctrl\n"); + return -EINVAL; + } + + ddc_ctrl = &display->ddc_ctrl; + ddc_data = &ddc_ctrl->ddc_data; + retry = ddc_data->retry; + + rc = sde_hdmi_ddc_read_retry(display); + if (!rc) + return rc; + + if (ddc_data->retry_align) { + ddc_data->retry = retry; + + ddc_data->request_len = 32 * ((ddc_data->data_len + 31) / 32); + rc = sde_hdmi_ddc_read_retry(display); + } + + return rc; +} /* hdmi_ddc_read */ + +int sde_hdmi_ddc_write(void *cb_data) +{ + int status; + struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl; + struct sde_hdmi_tx_ddc_data *ddc_data; + int busy_wait_us; + struct hdmi *hdmi; + struct sde_hdmi *display = (struct sde_hdmi *)cb_data; + + if (!display) { + SDE_ERROR("invalid input\n"); + return -EINVAL; + } + + hdmi = display->ctrl.ctrl; + ddc_ctrl = &display->ddc_ctrl; + + ddc_data = &ddc_ctrl->ddc_data; + + if (!ddc_data) { + SDE_ERROR("invalid input\n"); + return -EINVAL; + } + + if (!ddc_data->data_buf) { + status = -EINVAL; + SDE_ERROR("%s: invalid buf\n", ddc_data->what); + goto error; + } + + if (ddc_data->retry < 0) { + SDE_ERROR("invalid no. of retries %d\n", ddc_data->retry); + status = -EINVAL; + goto error; + } + + do { + if (ddc_data->hard_timeout) { + busy_wait_us = ddc_data->hard_timeout * HDMI_MS_TO_US; + hdmi->use_hard_timeout = true; + hdmi->busy_wait_us = busy_wait_us; + } + + status = hdmi_ddc_write(hdmi, + ddc_data->dev_addr, ddc_data->offset, + ddc_data->data_buf, ddc_data->data_len, + false); + + if (ddc_data->hard_timeout) + ddc_data->timeout_left = hdmi->timeout_count; + + if (ddc_data->hard_timeout && !hdmi->timeout_count) { + HDMI_UTIL_ERROR("%s timout\n", ddc_data->what); + status = -ETIMEDOUT; + } + + } while (status && ddc_data->retry--); + + if (status) { + HDMI_UTIL_ERROR("%s: failed status = %d\n", + ddc_data->what, status); + goto error; + } + + HDMI_UTIL_DEBUG("%s: success\n", ddc_data->what); +error: + return status; +} /* hdmi_ddc_write */ + +bool sde_hdmi_tx_is_hdcp_enabled(struct sde_hdmi *hdmi_ctrl) +{ + if (!hdmi_ctrl) { + SDE_ERROR("%s: invalid input\n", __func__); + return false; + } + + return (hdmi_ctrl->hdcp14_present || hdmi_ctrl->hdcp22_present) && + hdmi_ctrl->hdcp_ops; +} + +bool sde_hdmi_tx_is_encryption_set(struct sde_hdmi *hdmi_ctrl) +{ + bool enc_en = true; + u32 reg_val; + struct hdmi *hdmi; + + if (!hdmi_ctrl) { + SDE_ERROR("%s: invalid input\n", __func__); + goto end; + } + + hdmi = hdmi_ctrl->ctrl.ctrl; + + reg_val = hdmi_read(hdmi, HDMI_HDCP_CTRL2); + if ((reg_val & BIT(0)) && (reg_val & BIT(1))) + goto end; + + if (hdmi_read(hdmi, HDMI_CTRL) & BIT(2)) + goto end; + + return false; + +end: + return enc_en; +} /* sde_hdmi_tx_is_encryption_set */ + +bool sde_hdmi_tx_is_stream_shareable(struct sde_hdmi *hdmi_ctrl) +{ + bool ret; + + if (!hdmi_ctrl) { + SDE_ERROR("%s: invalid input\n", __func__); + return false; + } + + switch (hdmi_ctrl->enc_lvl) { + case HDCP_STATE_AUTH_ENC_NONE: + ret = true; + break; + case HDCP_STATE_AUTH_ENC_1X: + ret = sde_hdmi_tx_is_hdcp_enabled(hdmi_ctrl) && + hdmi_ctrl->auth_state; + break; + case HDCP_STATE_AUTH_ENC_2P2: + ret = hdmi_ctrl->hdcp22_present && + hdmi_ctrl->auth_state; + break; + default: + ret = false; + } + + return ret; +} + +bool sde_hdmi_tx_is_panel_on(struct sde_hdmi *hdmi_ctrl) +{ + struct hdmi *hdmi; + + if (!hdmi_ctrl) { + SDE_ERROR("%s: invalid input\n", __func__); + return false; + } + + hdmi = hdmi_ctrl->ctrl.ctrl; + + return hdmi_ctrl->connected && hdmi->power_on; +} + +int sde_hdmi_config_avmute(struct hdmi *hdmi, bool set) +{ + u32 av_mute_status; + bool av_pkt_en = false; + + if (!hdmi) { + SDE_ERROR("invalid HDMI Ctrl\n"); + return -ENODEV; + } + + av_mute_status = hdmi_read(hdmi, HDMI_GC); + + if (set) { + if (!(av_mute_status & BIT(0))) { + hdmi_write(hdmi, HDMI_GC, av_mute_status | BIT(0)); + av_pkt_en = true; + } + } else { + if (av_mute_status & BIT(0)) { + hdmi_write(hdmi, HDMI_GC, av_mute_status & ~BIT(0)); + av_pkt_en = true; + } + } + + /* Enable AV Mute tranmission here */ + if (av_pkt_en) + hdmi_write(hdmi, HDMI_VBI_PKT_CTRL, + hdmi_read(hdmi, HDMI_VBI_PKT_CTRL) | (BIT(4) & BIT(5))); + + pr_info("AVMUTE %s\n", set ? "set" : "cleared"); + + return 0; +} + +int _sde_hdmi_get_timeout_in_hysnc(void *hdmi_display, u32 timeout_ms) +{ + struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display; + struct drm_display_mode mode = display->mode; + /* + * pixel clock = h_total * v_total * fps + * 1 sec = pixel clock number of pixels are transmitted. + * time taken by one line (h_total) = 1s / (v_total * fps). + * lines for give time = (time_ms * 1000) / (1000000 / (v_total * fps)) + * = (time_ms * clock) / h_total + */ + + return (timeout_ms * mode.clock / mode.htotal); +} + +static void sde_hdmi_hdcp2p2_ddc_reset(struct sde_hdmi *hdmi_ctrl) +{ + u32 reg_val; + struct hdmi *hdmi = hdmi_ctrl->ctrl.ctrl; + + if (!hdmi) { + pr_err("Invalid parameters\n"); + return; + } + + /* + * Clear acks for DDC_REQ, DDC_DONE, DDC_FAILED, RXSTATUS_READY, + * RXSTATUS_MSG_SIZE + */ + reg_val = BIT(30) | BIT(17) | BIT(13) | BIT(9) | BIT(5) | BIT(1); + hdmi_write(hdmi, HDMI_DDC_INT_CTRL0, reg_val); + /* Reset DDC timers */ + reg_val = BIT(0) | hdmi_read(hdmi, HDMI_HDCP2P2_DDC_CTRL); + hdmi_write(hdmi, HDMI_HDCP2P2_DDC_CTRL, reg_val); + reg_val = hdmi_read(hdmi, HDMI_HDCP2P2_DDC_CTRL); + reg_val &= ~BIT(0); + hdmi_write(hdmi, HDMI_HDCP2P2_DDC_CTRL, reg_val); +} + +void sde_hdmi_hdcp2p2_ddc_disable(void *hdmi_display) +{ + struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display; + u32 reg_val; + struct hdmi *hdmi = display->ctrl.ctrl; + + if (!hdmi) { + pr_err("Invalid parameters\n"); + return; + } + + sde_hdmi_hdcp2p2_ddc_reset(display); + + /* Disable HW DDC access to RxStatus register */ + reg_val = hdmi_read(hdmi, HDMI_HW_DDC_CTRL); + reg_val &= ~(BIT(1) | BIT(0)); + + hdmi_write(hdmi, HDMI_HW_DDC_CTRL, reg_val); +} + +static void _sde_hdmi_scrambler_ddc_reset(struct hdmi *hdmi) +{ + u32 reg_val; + + /* clear ack and disable interrupts */ + reg_val = BIT(14) | BIT(9) | BIT(5) | BIT(1); + hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL2, reg_val); + + /* Reset DDC timers */ + reg_val = BIT(0) | hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL); + hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val); + + reg_val = hdmi_read(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL); + reg_val &= ~BIT(0); + hdmi_write(hdmi, REG_HDMI_SCRAMBLER_STATUS_DDC_CTRL, reg_val); +} + +void _sde_hdmi_scrambler_ddc_disable(void *hdmi_display) +{ + struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display; + u32 reg_val; + + struct hdmi *hdmi = display->ctrl.ctrl; + + if (!hdmi) { + pr_err("Invalid parameters\n"); + return; + } + + _sde_hdmi_scrambler_ddc_reset(hdmi); + /* Disable HW DDC access to RxStatus register */ + reg_val = hdmi_read(hdmi, REG_HDMI_HW_DDC_CTRL); + reg_val &= ~(BIT(8) | BIT(9)); + hdmi_write(hdmi, REG_HDMI_HW_DDC_CTRL, reg_val); +} + +void sde_hdmi_ddc_config(void *hdmi_display) +{ + struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display; + struct hdmi *hdmi = display->ctrl.ctrl; + + if (!hdmi) { + pr_err("Invalid parameters\n"); + return; + } + hdmi_write(hdmi, REG_HDMI_DDC_SPEED, + HDMI_DDC_SPEED_THRESHOLD(2) | + HDMI_DDC_SPEED_PRESCALE(10)); + + hdmi_write(hdmi, REG_HDMI_DDC_SETUP, + HDMI_DDC_SETUP_TIMEOUT(0xff)); + + /* enable reference timer for 19us */ + hdmi_write(hdmi, REG_HDMI_DDC_REF, + HDMI_DDC_REF_REFTIMER_ENABLE | + HDMI_DDC_REF_REFTIMER(19)); +} + +int sde_hdmi_hdcp2p2_read_rxstatus(void *hdmi_display) +{ + u32 reg_val; + u32 intr_en_mask; + u32 timeout; + u32 timer; + int rc = 0; + int busy_wait_us; + struct sde_hdmi_tx_hdcp2p2_ddc_data *data; + struct sde_hdmi *display = (struct sde_hdmi *)hdmi_display; + struct hdmi *hdmi = display->ctrl.ctrl; + struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl; + u32 rem; + + if (!hdmi) { + pr_err("Invalid ddc data\n"); + return -EINVAL; + } + + ddc_ctrl = &display->ddc_ctrl; + data = &ddc_ctrl->sde_hdcp2p2_ddc_data; + if (!data) { + pr_err("Invalid ddc data\n"); + return -EINVAL; + } + + rc = ddc_clear_irq(hdmi); + if (rc) { + pr_err("DDC clear irq failed\n"); + return rc; + } + intr_en_mask = data->intr_mask; + intr_en_mask |= BIT(HDCP2P2_RXSTATUS_DDC_FAILED_INTR_MASK); + + /* Disable short read for now, sinks don't support it */ + reg_val = hdmi_read(hdmi, HDMI_HDCP2P2_DDC_CTRL); + reg_val |= BIT(4); + hdmi_write(hdmi, HDMI_HDCP2P2_DDC_CTRL, reg_val); + /* + * Setup the DDC timers for HDMI_HDCP2P2_DDC_TIMER_CTRL1 and + * HDMI_HDCP2P2_DDC_TIMER_CTRL2. + * Following are the timers: + * 1. DDC_REQUEST_TIMER: Timeout in hsyncs in which to wait for the + * HDCP 2.2 sink to respond to an RxStatus request + * 2. DDC_URGENT_TIMER: Time period in hsyncs to issue an urgent flag + * when an RxStatus DDC request is made but not accepted by I2C + * engine + * 3. DDC_TIMEOUT_TIMER: Timeout in hsyncs which starts counting when + * a request is made and stops when it is accepted by DDC arbiter + */ + + timeout = data->timeout_hsync; + timer = data->periodic_timer_hsync; + + hdmi_write(hdmi, HDMI_HDCP2P2_DDC_TIMER_CTRL, timer); + /* Set both urgent and hw-timeout fields to the same value */ + hdmi_write(hdmi, HDMI_HDCP2P2_DDC_TIMER_CTRL2, + (timeout << 16 | timeout)); + /* enable interrupts */ + reg_val = intr_en_mask; + /* Clear interrupt status bits */ + reg_val |= intr_en_mask >> 1; + + hdmi_write(hdmi, HDMI_DDC_INT_CTRL0, reg_val); + reg_val = hdmi_read(hdmi, HDMI_DDC_INT_CTRL5); + /* clear and enable RxStatus read timeout */ + reg_val |= BIT(2) | BIT(1); + + hdmi_write(hdmi, HDMI_DDC_INT_CTRL5, reg_val); + /* + * Enable hardware DDC access to RxStatus register + * + * HDMI_HW_DDC_CTRL:Bits 1:0 (RXSTATUS_DDC_ENABLE) read like this: + * + * 0 = disable HW controlled DDC access to RxStatus + * 1 = automatic on when HDCP 2.2 is authenticated and loop based on + * request timer (i.e. the hardware will loop automatically) + * 2 = force on and loop based on request timer (hardware will loop) + * 3 = enable by sw trigger and loop until interrupt is generated for + * RxStatus.reauth_req, RxStatus.ready or RxStatus.message_Size. + * + * Depending on the value of ddc_data::poll_sink, we make the decision + * to use either SW_TRIGGER(3) (poll_sink = false) which means that the + * hardware will poll sink and generate interrupt when sink responds, + * or use AUTOMATIC_LOOP(1) (poll_sink = true) which will poll the sink + * based on request timer + */ + + reg_val = hdmi_read(hdmi, HDMI_HW_DDC_CTRL); + reg_val &= ~(BIT(1) | BIT(0)); + + busy_wait_us = data->timeout_ms * HDMI_MS_TO_US; + + /* read method: HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER */ + reg_val |= BIT(1) | BIT(0); + hdmi_write(hdmi, HDMI_HW_DDC_CTRL, reg_val); + + hdmi_write(hdmi, HDMI_HDCP2P2_DDC_SW_TRIGGER, 1); + if (data->wait) { + reinit_completion(&ddc_ctrl->rx_status_done); + rem = wait_for_completion_timeout(&ddc_ctrl->rx_status_done, + HZ); + data->timeout_left = jiffies_to_msecs(rem); + + if (!data->timeout_left) { + pr_err("sw ddc rxstatus timeout\n"); + rc = -ETIMEDOUT; + } + sde_hdmi_hdcp2p2_ddc_disable((void *)display); + } + return rc; +} diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h new file mode 100644 index 000000000000..8b69dd31f637 --- /dev/null +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef _SDE_HDMI_UTIL_H_ +#define _SDE_HDMI_UTIL_H_ + +#include <linux/types.h> +#include <linux/bitops.h> +#include <linux/debugfs.h> +#include <linux/of_device.h> +#include <linux/msm_ext_display.h> + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include "hdmi.h" +#include "sde_kms.h" +#include "sde_connector.h" +#include "msm_drv.h" +#include "sde_hdmi_regs.h" + +#ifdef HDMI_UTIL_DEBUG_ENABLE +#define HDMI_UTIL_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args) +#else +#define HDMI_UTIL_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args) +#endif + +#define HDMI_UTIL_ERROR(fmt, args...) SDE_ERROR(fmt, ##args) + +/* + * Offsets in HDMI_DDC_INT_CTRL0 register + * + * The HDMI_DDC_INT_CTRL0 register is intended for HDCP 2.2 RxStatus + * register manipulation. It reads like this: + * + * Bit 31: RXSTATUS_MESSAGE_SIZE_MASK (1 = generate interrupt when size > 0) + * Bit 30: RXSTATUS_MESSAGE_SIZE_ACK (1 = Acknowledge message size intr) + * Bits 29-20: RXSTATUS_MESSAGE_SIZE (Actual size of message available) + * Bits 19-18: RXSTATUS_READY_MASK (1 = generate interrupt when ready = 1 + * 2 = generate interrupt when ready = 0) + * Bit 17: RXSTATUS_READY_ACK (1 = Acknowledge ready bit interrupt) + * Bit 16: RXSTATUS_READY (1 = Rxstatus ready bit read is 1) + * Bit 15: RXSTATUS_READY_NOT (1 = Rxstatus ready bit read is 0) + * Bit 14: RXSTATUS_REAUTH_REQ_MASK (1 = generate interrupt when reauth is + * requested by sink) + * Bit 13: RXSTATUS_REAUTH_REQ_ACK (1 = Acknowledge Reauth req interrupt) + * Bit 12: RXSTATUS_REAUTH_REQ (1 = Rxstatus reauth req bit read is 1) + * Bit 10: RXSTATUS_DDC_FAILED_MASK (1 = generate interrupt when DDC + * tranasaction fails) + * Bit 9: RXSTATUS_DDC_FAILED_ACK (1 = Acknowledge ddc failure interrupt) + * Bit 8: RXSTATUS_DDC_FAILED (1 = DDC transaction failed) + * Bit 6: RXSTATUS_DDC_DONE_MASK (1 = generate interrupt when DDC + * transaction completes) + * Bit 5: RXSTATUS_DDC_DONE_ACK (1 = Acknowledge ddc done interrupt) + * Bit 4: RXSTATUS_DDC_DONE (1 = DDC transaction is done) + * Bit 2: RXSTATUS_DDC_REQ_MASK (1 = generate interrupt when DDC Read + * request for RXstatus is made) + * Bit 1: RXSTATUS_DDC_REQ_ACK (1 = Acknowledge Rxstatus read interrupt) + * Bit 0: RXSTATUS_DDC_REQ (1 = RXStatus DDC read request is made) + * + */ + +#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_SHIFT 20 +#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_MASK 0x3ff00000 +#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_ACK_SHIFT 30 +#define HDCP2P2_RXSTATUS_MESSAGE_SIZE_INTR_SHIFT 31 + +#define HDCP2P2_RXSTATUS_REAUTH_REQ_SHIFT 12 +#define HDCP2P2_RXSTATUS_REAUTH_REQ_MASK 1 +#define HDCP2P2_RXSTATUS_REAUTH_REQ_ACK_SHIFT 13 +#define HDCP2P2_RXSTATUS_REAUTH_REQ_INTR_SHIFT 14 + +#define HDCP2P2_RXSTATUS_READY_SHIFT 16 +#define HDCP2P2_RXSTATUS_READY_MASK 1 +#define HDCP2P2_RXSTATUS_READY_ACK_SHIFT 17 +#define HDCP2P2_RXSTATUS_READY_INTR_SHIFT 18 +#define HDCP2P2_RXSTATUS_READY_INTR_MASK 18 + +#define HDCP2P2_RXSTATUS_DDC_FAILED_SHIFT 8 +#define HDCP2P2_RXSTATUS_DDC_FAILED_ACKSHIFT 9 +#define HDCP2P2_RXSTATUS_DDC_FAILED_INTR_MASK 10 +#define HDCP2P2_RXSTATUS_DDC_DONE 6 + +/* default hsyncs for 4k@60 for 200ms */ +#define HDMI_DEFAULT_TIMEOUT_HSYNC 28571 + +#define HDMI_GET_MSB(x)(x >> 8) +#define HDMI_GET_LSB(x)(x & 0xff) + +/* + * Bits 1:0 in HDMI_HW_DDC_CTRL that dictate how the HDCP 2.2 RxStatus will be + * read by the hardware + */ +#define HDCP2P2_RXSTATUS_HW_DDC_DISABLE 0 +#define HDCP2P2_RXSTATUS_HW_DDC_AUTOMATIC_LOOP 1 +#define HDCP2P2_RXSTATUS_HW_DDC_FORCE_LOOP 2 +#define HDCP2P2_RXSTATUS_HW_DDC_SW_TRIGGER 3 + +struct sde_hdmi_tx_ddc_data { + char *what; + u8 *data_buf; + u32 data_len; + u32 dev_addr; + u32 offset; + u32 request_len; + u32 retry_align; + u32 hard_timeout; + u32 timeout_left; + int retry; +}; + +enum sde_hdmi_tx_hdcp2p2_rxstatus_intr_mask { + RXSTATUS_MESSAGE_SIZE = BIT(31), + RXSTATUS_READY = BIT(18), + RXSTATUS_REAUTH_REQ = BIT(14), +}; + +struct sde_hdmi_tx_hdcp2p2_ddc_data { + enum sde_hdmi_tx_hdcp2p2_rxstatus_intr_mask intr_mask; + u32 timeout_ms; + u32 timeout_hsync; + u32 periodic_timer_hsync; + u32 timeout_left; + u32 read_method; + u32 message_size; + bool encryption_ready; + bool ready; + bool reauth_req; + bool ddc_max_retries_fail; + bool ddc_done; + bool ddc_read_req; + bool ddc_timeout; + bool wait; + int irq_wait_count; + void (*link_cb)(void *data); + void *link_data; +}; + +struct sde_hdmi_tx_ddc_ctrl { + struct completion rx_status_done; + struct dss_io_data *io; + struct sde_hdmi_tx_ddc_data ddc_data; + struct sde_hdmi_tx_hdcp2p2_ddc_data sde_hdcp2p2_ddc_data; +}; + +/* DDC */ +int sde_hdmi_ddc_write(void *cb_data); +int sde_hdmi_ddc_read(void *cb_data); +int sde_hdmi_ddc_scrambling_isr(void *hdmi_display); +int _sde_hdmi_get_timeout_in_hysnc(void *hdmi_display, u32 timeout_ms); +void _sde_hdmi_scrambler_ddc_disable(void *hdmi_display); +void sde_hdmi_hdcp2p2_ddc_disable(void *hdmi_display); +int sde_hdmi_hdcp2p2_read_rxstatus(void *hdmi_display); +void sde_hdmi_ddc_config(void *hdmi_display); +int sde_hdmi_ddc_hdcp2p2_isr(void *hdmi_display); +void sde_hdmi_dump_regs(void *hdmi_display); +#endif /* _SDE_HDMI_UTIL_H_ */ diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 7915562057d6..7d660ba56594 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -95,7 +95,7 @@ static struct hdmi *hdmi_init(struct platform_device *pdev) struct hdmi_platform_config *config = pdev->dev.platform_data; struct hdmi *hdmi = NULL; struct resource *res; - int i, ret; + int i, ret = 0; hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); if (!hdmi) { @@ -119,9 +119,19 @@ static struct hdmi *hdmi_init(struct platform_device *pdev) } } + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, config->mmio_name); + if (!res) { + dev_err(&pdev->dev, "failed to find ctrl resource\n"); + ret = -ENOMEM; + goto fail; + } + hdmi->mmio_len = (u32)resource_size(res); hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI"); if (IS_ERR(hdmi->mmio)) { ret = PTR_ERR(hdmi->mmio); + dev_info(&pdev->dev, "can't map hdmi resource\n"); + hdmi->mmio = NULL; goto fail; } @@ -130,13 +140,39 @@ static struct hdmi *hdmi_init(struct platform_device *pdev) config->mmio_name); hdmi->mmio_phy_addr = res->start; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + config->qfprom_mmio_name); + + if (!res) { + dev_err(&pdev->dev, "failed to find qfprom resource\n"); + ret = -ENOMEM; + goto fail; + } + hdmi->qfprom_mmio_len = (u32)resource_size(res); + hdmi->qfprom_mmio = msm_ioremap(pdev, config->qfprom_mmio_name, "HDMI_QFPROM"); + if (IS_ERR(hdmi->qfprom_mmio)) { - dev_info(&pdev->dev, "can't find qfprom resource\n"); + dev_info(&pdev->dev, "can't map qfprom resource\n"); hdmi->qfprom_mmio = NULL; } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + config->hdcp_mmio_name); + if (!res) { + dev_err(&pdev->dev, "failed to find hdcp resource: %d\n", ret); + ret = -ENOMEM; + goto fail; + } + hdmi->hdcp_mmio_len = (u32)resource_size(res); + hdmi->hdcp_mmio = msm_ioremap(pdev, + config->hdcp_mmio_name, "HDMI_HDCP"); + if (IS_ERR(hdmi->hdcp_mmio)) { + dev_info(&pdev->dev, "can't map hdcp resource\n"); + hdmi->hdcp_mmio = NULL; + } + hdmi->hpd_regs = devm_kzalloc(&pdev->dev, sizeof(hdmi->hpd_regs[0]) * config->hpd_reg_cnt, GFP_KERNEL); if (!hdmi->hpd_regs) { @@ -468,6 +504,7 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) hdmi_cfg->mmio_name = "core_physical"; hdmi_cfg->qfprom_mmio_name = "qfprom_physical"; + hdmi_cfg->hdcp_mmio_name = "hdcp_physical"; hdmi_cfg->ddc_clk_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-clk"); hdmi_cfg->ddc_data_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-data"); hdmi_cfg->hpd_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-hpd"); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index 9ce8ff513210..8ca7b36ee0c8 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -27,6 +27,11 @@ #include "msm_drv.h" #include "hdmi.xml.h" +#define HDMI_SEC_TO_MS 1000 +#define HDMI_MS_TO_US 1000 +#define HDMI_SEC_TO_US (HDMI_SEC_TO_MS * HDMI_MS_TO_US) +#define HDMI_KHZ_TO_HZ 1000 +#define HDMI_BUSY_WAIT_DELAY_US 100 struct hdmi_phy; struct hdmi_platform_config; @@ -51,9 +56,14 @@ struct hdmi { /* video state: */ bool power_on; unsigned long int pixclock; + unsigned long int actual_pixclock; void __iomem *mmio; void __iomem *qfprom_mmio; + void __iomem *hdcp_mmio; + u32 mmio_len; + u32 qfprom_mmio_len; + u32 hdcp_mmio_len; phys_addr_t mmio_phy_addr; struct regulator **hpd_regs; @@ -72,10 +82,14 @@ struct hdmi { bool hdmi_mode; /* are we in hdmi mode? */ bool is_hdcp_supported; int irq; + void (*ddc_sw_done_cb)(void *data); + void *sw_done_cb_data; struct workqueue_struct *workq; struct hdmi_hdcp_ctrl *hdcp_ctrl; - + bool use_hard_timeout; + int busy_wait_us; + u32 timeout_count; /* * spinlock to protect registers shared by different execution * REG_HDMI_CTRL @@ -91,7 +105,7 @@ struct hdmi_platform_config { struct hdmi_phy *(*phy_init)(struct hdmi *hdmi); const char *mmio_name; const char *qfprom_mmio_name; - + const char *hdcp_mmio_name; /* regulators that need to be on for hpd: */ const char **hpd_reg_names; int hpd_reg_cnt; @@ -116,8 +130,20 @@ struct hdmi_platform_config { int mux_lpm_gpio; }; +struct hdmi_i2c_adapter { + struct i2c_adapter base; + struct hdmi *hdmi; + bool sw_done; + wait_queue_head_t ddc_event; +}; + void hdmi_set_mode(struct hdmi *hdmi, bool power_on); +#define to_hdmi_i2c_adapter(x) container_of(x, struct hdmi_i2c_adapter, base) + +int ddc_clear_irq(struct hdmi *hdmi); +void init_ddc(struct hdmi *hdmi); + static inline void hdmi_write(struct hdmi *hdmi, u32 reg, u32 data) { msm_writel(data, hdmi->mmio + reg); @@ -187,6 +213,13 @@ void hdmi_i2c_destroy(struct i2c_adapter *i2c); struct i2c_adapter *hdmi_i2c_init(struct hdmi *hdmi); /* + * DDC utility functions + */ +int hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset, + u8 *data, u16 data_len, bool self_retry); +int hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset, + u8 *data, u16 data_len, bool self_retry); +/* * hdcp */ struct hdmi_hdcp_ctrl *hdmi_hdcp_ctrl_init(struct hdmi *hdmi); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h index 0956617442af..ea485a2ec2cd 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h @@ -466,13 +466,13 @@ static inline uint32_t HDMI_DDC_REF_REFTIMER(uint32_t val) #define REG_HDMI_CEC_RD_FILTER 0x000002b0 #define REG_HDMI_ACTIVE_HSYNC 0x000002b4 -#define HDMI_ACTIVE_HSYNC_START__MASK 0x00000fff +#define HDMI_ACTIVE_HSYNC_START__MASK 0x00001fff #define HDMI_ACTIVE_HSYNC_START__SHIFT 0 static inline uint32_t HDMI_ACTIVE_HSYNC_START(uint32_t val) { return ((val) << HDMI_ACTIVE_HSYNC_START__SHIFT) & HDMI_ACTIVE_HSYNC_START__MASK; } -#define HDMI_ACTIVE_HSYNC_END__MASK 0x0fff0000 +#define HDMI_ACTIVE_HSYNC_END__MASK 0x1fff0000 #define HDMI_ACTIVE_HSYNC_END__SHIFT 16 static inline uint32_t HDMI_ACTIVE_HSYNC_END(uint32_t val) { @@ -480,13 +480,13 @@ static inline uint32_t HDMI_ACTIVE_HSYNC_END(uint32_t val) } #define REG_HDMI_ACTIVE_VSYNC 0x000002b8 -#define HDMI_ACTIVE_VSYNC_START__MASK 0x00000fff +#define HDMI_ACTIVE_VSYNC_START__MASK 0x00001fff #define HDMI_ACTIVE_VSYNC_START__SHIFT 0 static inline uint32_t HDMI_ACTIVE_VSYNC_START(uint32_t val) { return ((val) << HDMI_ACTIVE_VSYNC_START__SHIFT) & HDMI_ACTIVE_VSYNC_START__MASK; } -#define HDMI_ACTIVE_VSYNC_END__MASK 0x0fff0000 +#define HDMI_ACTIVE_VSYNC_END__MASK 0x1fff0000 #define HDMI_ACTIVE_VSYNC_END__SHIFT 16 static inline uint32_t HDMI_ACTIVE_VSYNC_END(uint32_t val) { @@ -494,13 +494,13 @@ static inline uint32_t HDMI_ACTIVE_VSYNC_END(uint32_t val) } #define REG_HDMI_VSYNC_ACTIVE_F2 0x000002bc -#define HDMI_VSYNC_ACTIVE_F2_START__MASK 0x00000fff +#define HDMI_VSYNC_ACTIVE_F2_START__MASK 0x00001fff #define HDMI_VSYNC_ACTIVE_F2_START__SHIFT 0 static inline uint32_t HDMI_VSYNC_ACTIVE_F2_START(uint32_t val) { return ((val) << HDMI_VSYNC_ACTIVE_F2_START__SHIFT) & HDMI_VSYNC_ACTIVE_F2_START__MASK; } -#define HDMI_VSYNC_ACTIVE_F2_END__MASK 0x0fff0000 +#define HDMI_VSYNC_ACTIVE_F2_END__MASK 0x1fff0000 #define HDMI_VSYNC_ACTIVE_F2_END__SHIFT 16 static inline uint32_t HDMI_VSYNC_ACTIVE_F2_END(uint32_t val) { @@ -508,13 +508,13 @@ static inline uint32_t HDMI_VSYNC_ACTIVE_F2_END(uint32_t val) } #define REG_HDMI_TOTAL 0x000002c0 -#define HDMI_TOTAL_H_TOTAL__MASK 0x00000fff +#define HDMI_TOTAL_H_TOTAL__MASK 0x00001fff #define HDMI_TOTAL_H_TOTAL__SHIFT 0 static inline uint32_t HDMI_TOTAL_H_TOTAL(uint32_t val) { return ((val) << HDMI_TOTAL_H_TOTAL__SHIFT) & HDMI_TOTAL_H_TOTAL__MASK; } -#define HDMI_TOTAL_V_TOTAL__MASK 0x0fff0000 +#define HDMI_TOTAL_V_TOTAL__MASK 0x1fff0000 #define HDMI_TOTAL_V_TOTAL__SHIFT 16 static inline uint32_t HDMI_TOTAL_V_TOTAL(uint32_t val) { @@ -522,7 +522,7 @@ static inline uint32_t HDMI_TOTAL_V_TOTAL(uint32_t val) } #define REG_HDMI_VSYNC_TOTAL_F2 0x000002c4 -#define HDMI_VSYNC_TOTAL_F2_V_TOTAL__MASK 0x00000fff +#define HDMI_VSYNC_TOTAL_F2_V_TOTAL__MASK 0x00001fff #define HDMI_VSYNC_TOTAL_F2_V_TOTAL__SHIFT 0 static inline uint32_t HDMI_VSYNC_TOTAL_F2_V_TOTAL(uint32_t val) { diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c index e56a8675c0a4..66be37bea4f6 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -85,84 +85,6 @@ struct hdmi_hdcp_ctrl { bool max_dev_exceeded; }; -static int hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset, - u8 *data, u16 data_len) -{ - int rc; - int retry = 5; - struct i2c_msg msgs[] = { - { - .addr = addr >> 1, - .flags = 0, - .len = 1, - .buf = &offset, - }, { - .addr = addr >> 1, - .flags = I2C_M_RD, - .len = data_len, - .buf = data, - } - }; - - DBG("Start DDC read"); -retry: - rc = i2c_transfer(hdmi->i2c, msgs, 2); - - retry--; - if (rc == 2) - rc = 0; - else if (retry > 0) - goto retry; - else - rc = -EIO; - - DBG("End DDC read %d", rc); - - return rc; -} - -#define HDCP_DDC_WRITE_MAX_BYTE_NUM 32 - -static int hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset, - u8 *data, u16 data_len) -{ - int rc; - int retry = 10; - u8 buf[HDCP_DDC_WRITE_MAX_BYTE_NUM]; - struct i2c_msg msgs[] = { - { - .addr = addr >> 1, - .flags = 0, - .len = 1, - } - }; - - DBG("Start DDC write"); - if (data_len > (HDCP_DDC_WRITE_MAX_BYTE_NUM - 1)) { - pr_err("%s: write size too big\n", __func__); - return -ERANGE; - } - - buf[0] = offset; - memcpy(&buf[1], data, data_len); - msgs[0].buf = buf; - msgs[0].len = data_len + 1; -retry: - rc = i2c_transfer(hdmi->i2c, msgs, 1); - - retry--; - if (rc == 1) - rc = 0; - else if (retry > 0) - goto retry; - else - rc = -EIO; - - DBG("End DDC write %d", rc); - - return rc; -} - static int hdmi_hdcp_scm_wr(struct hdmi_hdcp_ctrl *hdcp_ctrl, u32 *preg, u32 *pdata, u32 count) { diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c index f4ab7f70fed1..c65cc908b882 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c @@ -17,66 +17,16 @@ #include "hdmi.h" -struct hdmi_i2c_adapter { - struct i2c_adapter base; - struct hdmi *hdmi; - bool sw_done; - wait_queue_head_t ddc_event; -}; -#define to_hdmi_i2c_adapter(x) container_of(x, struct hdmi_i2c_adapter, base) - -static void init_ddc(struct hdmi_i2c_adapter *hdmi_i2c) -{ - struct hdmi *hdmi = hdmi_i2c->hdmi; - - hdmi_write(hdmi, REG_HDMI_DDC_CTRL, - HDMI_DDC_CTRL_SW_STATUS_RESET); - hdmi_write(hdmi, REG_HDMI_DDC_CTRL, - HDMI_DDC_CTRL_SOFT_RESET); - - hdmi_write(hdmi, REG_HDMI_DDC_SPEED, - HDMI_DDC_SPEED_THRESHOLD(2) | - HDMI_DDC_SPEED_PRESCALE(10)); - - hdmi_write(hdmi, REG_HDMI_DDC_SETUP, - HDMI_DDC_SETUP_TIMEOUT(0xff)); +#define MAX_TRANSACTIONS 4 - /* enable reference timer for 27us */ - hdmi_write(hdmi, REG_HDMI_DDC_REF, - HDMI_DDC_REF_REFTIMER_ENABLE | - HDMI_DDC_REF_REFTIMER(27)); -} +#define SDE_DDC_TXN_CNT_MASK 0x07ff0000 +#define SDE_DDC_TXN_CNT_SHIFT 16 -static int ddc_clear_irq(struct hdmi_i2c_adapter *hdmi_i2c) +static inline uint32_t SDE_HDMI_I2C_TRANSACTION_REG_CNT(uint32_t val) { - struct hdmi *hdmi = hdmi_i2c->hdmi; - struct drm_device *dev = hdmi->dev; - uint32_t retry = 0xffff; - uint32_t ddc_int_ctrl; - - do { - --retry; - - hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL, - HDMI_DDC_INT_CTRL_SW_DONE_ACK | - HDMI_DDC_INT_CTRL_SW_DONE_MASK); - - ddc_int_ctrl = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL); - - } while ((ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_INT) && retry); - - if (!retry) { - dev_err(dev->dev, "timeout waiting for DDC\n"); - return -ETIMEDOUT; - } - - hdmi_i2c->sw_done = false; - - return 0; + return ((val) << SDE_DDC_TXN_CNT_SHIFT) & SDE_DDC_TXN_CNT_MASK; } -#define MAX_TRANSACTIONS 4 - static bool sw_done(struct hdmi_i2c_adapter *hdmi_i2c) { struct hdmi *hdmi = hdmi_i2c->hdmi; @@ -115,12 +65,13 @@ static int hdmi_i2c_xfer(struct i2c_adapter *i2c, WARN_ON(!(hdmi_read(hdmi, REG_HDMI_CTRL) & HDMI_CTRL_ENABLE)); + if (num == 0) return num; - init_ddc(hdmi_i2c); + init_ddc(hdmi); - ret = ddc_clear_irq(hdmi_i2c); + ret = ddc_clear_irq(hdmi); if (ret) return ret; @@ -155,7 +106,7 @@ static int hdmi_i2c_xfer(struct i2c_adapter *i2c, } } - i2c_trans = HDMI_I2C_TRANSACTION_REG_CNT(p->len) | + i2c_trans = SDE_HDMI_I2C_TRANSACTION_REG_CNT(p->len) | HDMI_I2C_TRANSACTION_REG_RW( (p->flags & I2C_M_RD) ? DDC_READ : DDC_WRITE) | HDMI_I2C_TRANSACTION_REG_START; @@ -177,9 +128,13 @@ static int hdmi_i2c_xfer(struct i2c_adapter *i2c, ret = -ETIMEDOUT; dev_warn(dev->dev, "DDC timeout: %d\n", ret); DBG("sw_status=%08x, hw_status=%08x, int_ctrl=%08x", - hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS), - hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS), - hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL)); + hdmi_read(hdmi, REG_HDMI_DDC_SW_STATUS), + hdmi_read(hdmi, REG_HDMI_DDC_HW_STATUS), + hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL)); + if (hdmi->use_hard_timeout) { + hdmi->use_hard_timeout = false; + hdmi->timeout_count = 0; + } return ret; } @@ -213,6 +168,10 @@ static int hdmi_i2c_xfer(struct i2c_adapter *i2c, } } + if (hdmi->use_hard_timeout) { + hdmi->use_hard_timeout = false; + hdmi->timeout_count = jiffies_to_msecs(ret); + } return i; } diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_util.c b/drivers/gpu/drm/msm/hdmi/hdmi_util.c new file mode 100644 index 000000000000..c7cfa38ed3ad --- /dev/null +++ b/drivers/gpu/drm/msm/hdmi/hdmi_util.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/of_irq.h> +#include "hdmi.h" + +void init_ddc(struct hdmi *hdmi) +{ + hdmi_write(hdmi, REG_HDMI_DDC_CTRL, + HDMI_DDC_CTRL_SW_STATUS_RESET); + hdmi_write(hdmi, REG_HDMI_DDC_CTRL, + HDMI_DDC_CTRL_SOFT_RESET); + + hdmi_write(hdmi, REG_HDMI_DDC_SPEED, + HDMI_DDC_SPEED_THRESHOLD(2) | + HDMI_DDC_SPEED_PRESCALE(10)); + + hdmi_write(hdmi, REG_HDMI_DDC_SETUP, + HDMI_DDC_SETUP_TIMEOUT(0xff)); + + /* enable reference timer for 19us */ + hdmi_write(hdmi, REG_HDMI_DDC_REF, + HDMI_DDC_REF_REFTIMER_ENABLE | + HDMI_DDC_REF_REFTIMER(19)); +} + +int ddc_clear_irq(struct hdmi *hdmi) +{ + struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(hdmi->i2c); + struct drm_device *dev = hdmi->dev; + uint32_t retry = 0xffff; + uint32_t ddc_int_ctrl; + + do { + --retry; + + hdmi_write(hdmi, REG_HDMI_DDC_INT_CTRL, + HDMI_DDC_INT_CTRL_SW_DONE_ACK | + HDMI_DDC_INT_CTRL_SW_DONE_MASK); + + ddc_int_ctrl = hdmi_read(hdmi, REG_HDMI_DDC_INT_CTRL); + + } while ((ddc_int_ctrl & HDMI_DDC_INT_CTRL_SW_DONE_INT) && retry); + + if (!retry) { + dev_err(dev->dev, "timeout waiting for DDC\n"); + return -ETIMEDOUT; + } + + hdmi_i2c->sw_done = false; + + return 0; +} + +int hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset, +u8 *data, u16 data_len, bool self_retry) +{ + int rc; + int retry = 10; + struct i2c_msg msgs[] = { + { + .addr = addr >> 1, + .flags = 0, + .len = 1, + .buf = &offset, + }, { + .addr = addr >> 1, + .flags = I2C_M_RD, + .len = data_len, + .buf = data, + } + }; + + DBG("Start DDC read"); +retry: + rc = i2c_transfer(hdmi->i2c, msgs, 2); + retry--; + + if (rc == 2) + rc = 0; + else if (self_retry && (retry > 0)) + goto retry; + else + rc = -EIO; + + DBG("End DDC read %d", rc); + + return rc; +} + +#define HDCP_DDC_WRITE_MAX_BYTE_NUM 1024 + +int hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset, + u8 *data, u16 data_len, bool self_retry) +{ + int rc; + int retry = 10; + u8 buf[HDCP_DDC_WRITE_MAX_BYTE_NUM]; + struct i2c_msg msgs[] = { + { + .addr = addr >> 1, + .flags = 0, + .len = 1, + } + }; + + pr_debug("TESTING ! REMOVE RETRY Start DDC write"); + if (data_len > (HDCP_DDC_WRITE_MAX_BYTE_NUM - 1)) { + pr_err("%s: write size too big\n", __func__); + return -ERANGE; + } + + buf[0] = offset; + memcpy(&buf[1], data, data_len); + msgs[0].buf = buf; + msgs[0].len = data_len + 1; +retry: + rc = i2c_transfer(hdmi->i2c, msgs, 1); + retry--; + if (rc == 1) + rc = 0; + else if (self_retry && (retry > 0)) + goto retry; + else + rc = -EIO; + + DBG("End DDC write %d", rc); + + return rc; +} diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index caad2be87ae4..e5f42fe983c1 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -392,7 +392,7 @@ static void update_cursor(struct drm_crtc *crtc) if (next_bo) { /* take a obj ref + iova ref when we start scanning out: */ drm_gem_object_reference(next_bo); - msm_gem_get_iova_locked(next_bo, mdp4_kms->aspace, + msm_gem_get_iova(next_bo, mdp4_kms->aspace, &iova); /* enable cursor: */ diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index 119221eacb43..40509434a913 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -536,9 +536,7 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev) goto fail; } - mutex_lock(&dev->struct_mutex); mdp4_kms->blank_cursor_bo = msm_gem_new(dev, SZ_16K, MSM_BO_WC); - mutex_unlock(&dev->struct_mutex); if (IS_ERR(mdp4_kms->blank_cursor_bo)) { ret = PTR_ERR(mdp4_kms->blank_cursor_bo); dev_err(dev->dev, "could not allocate blank-cursor bo: %d\n", ret); diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index fa746d71cd3b..0c119ec5d97c 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -19,6 +19,7 @@ #include "msm_drv.h" #include "msm_kms.h" #include "msm_gem.h" +#include "sde_trace.h" struct msm_commit { struct drm_device *dev; @@ -108,6 +109,7 @@ msm_disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) struct drm_crtc_state *old_crtc_state; int i; + SDE_ATRACE_BEGIN("msm_disable"); for_each_connector_in_state(old_state, connector, old_conn_state, i) { const struct drm_encoder_helper_funcs *funcs; struct drm_encoder *encoder; @@ -188,6 +190,7 @@ msm_disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state) else funcs->dpms(crtc, DRM_MODE_DPMS_OFF); } + SDE_ATRACE_END("msm_disable"); } static void @@ -297,6 +300,7 @@ static void msm_atomic_helper_commit_modeset_enables(struct drm_device *dev, int bridge_enable_count = 0; int i; + SDE_ATRACE_BEGIN("msm_enable"); for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { const struct drm_crtc_helper_funcs *funcs; @@ -364,8 +368,10 @@ static void msm_atomic_helper_commit_modeset_enables(struct drm_device *dev, } /* If no bridges were pre_enabled, skip iterating over them again */ - if (bridge_enable_count == 0) + if (bridge_enable_count == 0) { + SDE_ATRACE_END("msm_enable"); return; + } for_each_connector_in_state(old_state, connector, old_conn_state, i) { struct drm_encoder *encoder; @@ -385,6 +391,7 @@ static void msm_atomic_helper_commit_modeset_enables(struct drm_device *dev, drm_bridge_enable(encoder->bridge); } + SDE_ATRACE_END("msm_enable"); } /* The (potentially) asynchronous part of the commit. At this point @@ -457,7 +464,9 @@ static void _msm_drm_commit_work_cb(struct kthread_work *work) commit = container_of(work, struct msm_commit, commit_work); + SDE_ATRACE_BEGIN("complete_commit"); complete_commit(commit); + SDE_ATRACE_END("complete_commit"); } static struct msm_commit *commit_init(struct drm_atomic_state *state) @@ -553,9 +562,12 @@ int msm_atomic_commit(struct drm_device *dev, struct msm_commit *commit; int i, ret; + SDE_ATRACE_BEGIN("atomic_commit"); ret = drm_atomic_helper_prepare_planes(dev, state); - if (ret) + if (ret) { + SDE_ATRACE_END("atomic_commit"); return ret; + } commit = commit_init(state); if (IS_ERR_OR_NULL(commit)) { @@ -635,6 +647,7 @@ int msm_atomic_commit(struct drm_device *dev, if (async) { msm_queue_fence_cb(dev, &commit->fence_cb, commit->fence); + SDE_ATRACE_END("atomic_commit"); return 0; } @@ -645,9 +658,11 @@ int msm_atomic_commit(struct drm_device *dev, complete_commit(commit); + SDE_ATRACE_END("atomic_commit"); return 0; error: drm_atomic_helper_cleanup_planes(dev, state); + SDE_ATRACE_END("atomic_commit"); return ret; } diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 665f8db0e04f..2280f8cb7183 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -26,11 +26,68 @@ #include "msm_gem.h" #include "msm_mmu.h" +static void msm_drm_helper_hotplug_event(struct drm_device *dev) +{ + struct drm_connector *connector; + char *event_string; + char const *connector_name; + char *envp[2]; + + if (!dev) { + DRM_ERROR("hotplug_event failed, invalid input\n"); + return; + } + + if (!dev->mode_config.poll_enabled) + return; + + event_string = kzalloc(SZ_4K, GFP_KERNEL); + if (!event_string) { + DRM_ERROR("failed to allocate event string\n"); + return; + } + + mutex_lock(&dev->mode_config.mutex); + drm_for_each_connector(connector, dev) { + /* Only handle HPD capable connectors. */ + if (!(connector->polled & DRM_CONNECTOR_POLL_HPD)) + continue; + + connector->status = connector->funcs->detect(connector, false); + + if (connector->name) + connector_name = connector->name; + else + connector_name = "unknown"; + + snprintf(event_string, SZ_4K, "name=%s status=%s\n", + connector_name, + drm_get_connector_status_name(connector->status)); + DRM_DEBUG("generating hotplug event [%s]\n", event_string); + envp[0] = event_string; + envp[1] = NULL; + kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, + envp); + } + mutex_unlock(&dev->mode_config.mutex); + kfree(event_string); +} + static void msm_fb_output_poll_changed(struct drm_device *dev) { - struct msm_drm_private *priv = dev->dev_private; + struct msm_drm_private *priv = NULL; + + if (!dev) { + DRM_ERROR("output_poll_changed failed, invalid input\n"); + return; + } + + priv = dev->dev_private; + if (priv->fbdev) drm_fb_helper_hotplug_event(priv->fbdev); + else + msm_drm_helper_hotplug_event(dev); } static const struct drm_mode_config_funcs mode_config_funcs = { @@ -259,8 +316,8 @@ static int get_mdp_ver(struct platform_device *pdev) static const struct of_device_id match_types[] = { { .compatible = "qcom,sde-kms", .data = (void *)KMS_SDE, - /* end node */ - } }; + }, + {} }; struct device *dev = &pdev->dev; const struct of_device_id *match; match = of_match_node(match_types, dev->of_node); @@ -322,6 +379,7 @@ static int msm_init_vram(struct drm_device *dev) priv->vram.size = size; drm_mm_init(&priv->vram.mm, 0, (size >> PAGE_SHIFT) - 1); + spin_lock_init(&priv->vram.lock); dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs); dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs); @@ -390,6 +448,8 @@ static int msm_load(struct drm_device *dev, unsigned long flags) INIT_LIST_HEAD(&priv->vblank_ctrl.event_list); init_kthread_work(&priv->vblank_ctrl.work, vblank_ctrl_worker); spin_lock_init(&priv->vblank_ctrl.lock); + hash_init(priv->mn_hash); + mutex_init(&priv->mn_lock); drm_mode_config_init(dev); @@ -558,7 +618,8 @@ static struct msm_file_private *setup_pagetable(struct msm_drm_private *priv) return ERR_PTR(-ENOMEM); ctx->aspace = msm_gem_address_space_create_instance( - priv->gpu->aspace->mmu, "gpu", 0x100000000, 0x1ffffffff); + priv->gpu->aspace->mmu, "gpu", 0x100000000ULL, + TASK_SIZE_64 - 1); if (IS_ERR(ctx->aspace)) { int ret = PTR_ERR(ctx->aspace); @@ -598,7 +659,10 @@ static int msm_open(struct drm_device *dev, struct drm_file *file) if (IS_ERR(ctx)) return PTR_ERR(ctx); - INIT_LIST_HEAD(&ctx->counters); + if (ctx) + INIT_LIST_HEAD(&ctx->counters); + + msm_submitqueue_init(ctx); file->driver_priv = ctx; @@ -628,15 +692,19 @@ static void msm_postclose(struct drm_device *dev, struct drm_file *file) if (kms && kms->funcs && kms->funcs->postclose) kms->funcs->postclose(kms, file); - if (priv->gpu) + if (!ctx) + return; + + msm_submitqueue_close(ctx); + + if (priv->gpu) { msm_gpu_cleanup_counters(priv->gpu, ctx); - mutex_lock(&dev->struct_mutex); - if (ctx && ctx->aspace && ctx->aspace != priv->gpu->aspace) { - ctx->aspace->mmu->funcs->detach(ctx->aspace->mmu); - msm_gem_address_space_put(ctx->aspace); + if (ctx->aspace && ctx->aspace != priv->gpu->aspace) { + ctx->aspace->mmu->funcs->detach(ctx->aspace->mmu); + msm_gem_address_space_put(ctx->aspace); + } } - mutex_unlock(&dev->struct_mutex); kfree(ctx); } @@ -1142,6 +1210,20 @@ static int msm_ioctl_gem_new(struct drm_device *dev, void *data, args->flags, &args->handle); } +static int msm_ioctl_gem_svm_new(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_msm_gem_svm_new *args = data; + + if (args->flags & ~MSM_BO_FLAGS) { + DRM_ERROR("invalid flags: %08x\n", args->flags); + return -EINVAL; + } + + return msm_gem_svm_new_handle(dev, file, args->hostptr, args->size, + args->flags, &args->handle); +} + static inline ktime_t to_ktime(struct drm_msm_timespec timeout) { return ktime_set(timeout.tv_sec, timeout.tv_nsec); @@ -1194,6 +1276,7 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data, { struct drm_msm_gem_info *args = data; struct drm_gem_object *obj; + struct msm_gem_object *msm_obj; struct msm_file_private *ctx = file->driver_priv; int ret = 0; @@ -1204,10 +1287,10 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data, if (!obj) return -ENOENT; + msm_obj = to_msm_bo(obj); if (args->flags & MSM_INFO_IOVA) { struct msm_gem_address_space *aspace = NULL; struct msm_drm_private *priv = dev->dev_private; - struct msm_gem_object *msm_obj = to_msm_bo(obj); uint64_t iova; if (msm_obj->flags & MSM_BO_SECURE && priv->gpu) @@ -1224,6 +1307,14 @@ static int msm_ioctl_gem_info(struct drm_device *dev, void *data, if (!ret) args->offset = iova; } else { + if (msm_obj->flags & MSM_BO_SVM) { + /* + * Offset for an SVM object is not needed as they are + * already mmap'ed before the SVM ioctl is invoked. + */ + ret = -EACCES; + goto out; + } args->offset = msm_gem_mmap_offset(obj); } @@ -1658,6 +1749,51 @@ static int msm_ioctl_counter_read(struct drm_device *dev, void *data, return -ENODEV; } + +static int msm_ioctl_submitqueue_new(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_msm_submitqueue *args = data; + struct msm_drm_private *priv = dev->dev_private; + struct msm_gpu *gpu = priv->gpu; + + if (args->flags & ~MSM_SUBMITQUEUE_FLAGS) + return -EINVAL; + + if (!file->is_master && args->prio >= gpu->nr_rings - 1) { + DRM_ERROR("Only DRM master can set highest priority ringbuffer\n"); + return -EPERM; + } + + if (args->flags & MSM_SUBMITQUEUE_BYPASS_QOS_TIMEOUT && + !capable(CAP_SYS_ADMIN)) { + DRM_ERROR( + "Only CAP_SYS_ADMIN processes can bypass the timer\n"); + return -EPERM; + } + + return msm_submitqueue_create(file->driver_priv, args->prio, + args->flags, &args->id); +} + +static int msm_ioctl_submitqueue_query(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_msm_submitqueue_query *args = data; + void __user *ptr = (void __user *)(uintptr_t) args->data; + + return msm_submitqueue_query(file->driver_priv, args->id, + args->param, ptr, args->len); +} + +static int msm_ioctl_submitqueue_close(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_msm_submitqueue *args = data; + + return msm_submitqueue_remove(file->driver_priv, args->id); +} + int msm_release(struct inode *inode, struct file *filp) { struct drm_file *file_priv = filp->private_data; @@ -1701,6 +1837,14 @@ static const struct drm_ioctl_desc msm_ioctls[] = { DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_GEM_SYNC, msm_ioctl_gem_sync, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_GEM_SVM_NEW, msm_ioctl_gem_svm_new, + DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_NEW, msm_ioctl_submitqueue_new, + DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_CLOSE, msm_ioctl_submitqueue_close, + DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_QUERY, msm_ioctl_submitqueue_query, + DRM_AUTH|DRM_RENDER_ALLOW), }; static const struct vm_operations_struct vm_ops = { @@ -1753,6 +1897,7 @@ static struct drm_driver msm_driver = { .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_export = drm_gem_prime_export, .gem_prime_import = drm_gem_prime_import, + .gem_prime_res_obj = msm_gem_prime_res_obj, .gem_prime_pin = msm_gem_prime_pin, .gem_prime_unpin = msm_gem_prime_unpin, .gem_prime_get_sg_table = msm_gem_prime_get_sg_table, @@ -1952,6 +2097,7 @@ static struct platform_driver msm_platform_driver = { .name = "msm_drm", .of_match_table = dt_match, .pm = &msm_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .id_table = msm_id, }; @@ -1969,6 +2115,7 @@ void __exit adreno_unregister(void) static int __init msm_drm_register(void) { DBG("init"); + msm_smmu_driver_init(); msm_dsi_register(); msm_edp_register(); hdmi_register(); @@ -1984,6 +2131,7 @@ static void __exit msm_drm_unregister(void) adreno_unregister(); msm_edp_unregister(); msm_dsi_unregister(); + msm_smmu_driver_cleanup(); } module_init(msm_drm_register); diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 94f429cb83a7..d696f05e0459 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -34,6 +34,7 @@ #include <linux/of_graph.h> #include <linux/of_device.h> #include <linux/sde_io_util.h> +#include <linux/hashtable.h> #include <asm/sizes.h> #include <linux/kthread.h> @@ -77,6 +78,9 @@ struct msm_gem_vma; struct msm_file_private { struct msm_gem_address_space *aspace; struct list_head counters; + rwlock_t queuelock; + struct list_head submitqueues; + int queueid; }; enum msm_mdp_plane_property { @@ -111,6 +115,7 @@ enum msm_mdp_plane_property { PLANE_PROP_ROTATION, PLANE_PROP_BLEND_OP, PLANE_PROP_SRC_CONFIG, + PLANE_PROP_FB_TRANSLATION_MODE, /* total # of properties */ PLANE_PROP_COUNT @@ -129,6 +134,7 @@ enum msm_mdp_crtc_property { CRTC_PROP_CORE_CLK, CRTC_PROP_CORE_AB, CRTC_PROP_CORE_IB, + CRTC_PROP_SECURITY_LEVEL, /* total # of properties */ CRTC_PROP_COUNT @@ -137,6 +143,8 @@ enum msm_mdp_crtc_property { enum msm_mdp_conn_property { /* blob properties, always put these first */ CONNECTOR_PROP_SDE_INFO, + CONNECTOR_PROP_HDR_INFO, + CONNECTOR_PROP_HDR_METADATA, /* # of blob properties */ CONNECTOR_PROP_BLOBCOUNT, @@ -149,6 +157,7 @@ enum msm_mdp_conn_property { CONNECTOR_PROP_DST_W, CONNECTOR_PROP_DST_H, CONNECTOR_PROP_PLL_DELTA, + CONNECTOR_PROP_PLL_ENABLE, /* enum/bitmask properties */ CONNECTOR_PROP_TOPOLOGY_NAME, @@ -227,6 +236,14 @@ struct msm_display_info { }; /** + * struct - msm_display_kickoff_params - info for display features at kickoff + * @hdr_metadata: HDR metadata info passed from userspace + */ +struct msm_display_kickoff_params { + struct drm_msm_ext_panel_hdr_metadata *hdr_metadata; +}; + +/** * struct msm_drm_event - defines custom event notification struct * @base: base object required for event notification by DRM framework. * @event: event object required for event notification by DRM framework. @@ -325,6 +342,11 @@ struct msm_drm_private { unsigned int num_connectors; struct drm_connector *connectors[MAX_CONNECTORS]; + /* hash to store mm_struct to msm_mmu_notifier mappings */ + DECLARE_HASHTABLE(mn_hash, 7); + /* protects mn_hash and the msm_mmu_notifier for the process */ + struct mutex mn_lock; + /* Properties */ struct drm_property *plane_property[PLANE_PROP_COUNT]; struct drm_property *crtc_property[CRTC_PROP_COUNT]; @@ -341,6 +363,7 @@ struct msm_drm_private { * and position mm_node->start is in # of pages: */ struct drm_mm mm; + spinlock_t lock; /* Protects drm_mm node allocation/removal */ } vram; struct msm_vblank_ctrl vblank_ctrl; @@ -403,10 +426,15 @@ void msm_update_fence(struct drm_device *dev, uint32_t fence); void msm_gem_unmap_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, struct sg_table *sgt, - void *priv); + void *priv, bool invalidated); int msm_gem_map_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, struct sg_table *sgt, void *priv, unsigned int flags); +int msm_gem_reserve_iova(struct msm_gem_address_space *aspace, + struct msm_gem_vma *domain, + uint64_t hostptr, uint64_t size); +void msm_gem_release_iova(struct msm_gem_address_space *aspace, + struct msm_gem_vma *vma); void msm_gem_address_space_put(struct msm_gem_address_space *aspace); @@ -423,6 +451,7 @@ struct msm_gem_address_space * msm_gem_smmu_address_space_create(struct device *dev, struct msm_mmu *mmu, const char *name); +void msm_gem_submit_free(struct msm_gem_submit *submit); int msm_ioctl_gem_submit(struct drm_device *dev, void *data, struct drm_file *file); @@ -431,8 +460,6 @@ int msm_gem_mmap_obj(struct drm_gem_object *obj, int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma); int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj); -int msm_gem_get_iova_locked(struct drm_gem_object *obj, - struct msm_gem_address_space *aspace, uint64_t *iova); int msm_gem_get_iova(struct drm_gem_object *obj, struct msm_gem_address_space *aspace, uint64_t *iova); uint64_t msm_gem_iova(struct drm_gem_object *obj, @@ -449,11 +476,11 @@ struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj); void *msm_gem_prime_vmap(struct drm_gem_object *obj); void msm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); int msm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); +struct reservation_object *msm_gem_prime_res_obj(struct drm_gem_object *obj); struct drm_gem_object *msm_gem_prime_import_sg_table(struct drm_device *dev, struct dma_buf_attachment *attach, struct sg_table *sg); int msm_gem_prime_pin(struct drm_gem_object *obj); void msm_gem_prime_unpin(struct drm_gem_object *obj); -void *msm_gem_vaddr_locked(struct drm_gem_object *obj); void *msm_gem_vaddr(struct drm_gem_object *obj); int msm_gem_queue_inactive_cb(struct drm_gem_object *obj, struct msm_fence_cb *cb); @@ -468,10 +495,23 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, uint32_t size, uint32_t flags, uint32_t *handle); struct drm_gem_object *msm_gem_new(struct drm_device *dev, uint32_t size, uint32_t flags); +struct drm_gem_object *msm_gem_new_locked(struct drm_device *dev, + uint32_t size, uint32_t flags); struct drm_gem_object *msm_gem_import(struct drm_device *dev, uint32_t size, struct sg_table *sgt, u32 flags); void msm_gem_sync(struct drm_gem_object *obj, u32 op); - +int msm_gem_svm_new_handle(struct drm_device *dev, struct drm_file *file, + uint64_t hostptr, uint64_t size, + uint32_t flags, uint32_t *handle); +struct drm_gem_object *msm_gem_svm_new(struct drm_device *dev, + struct drm_file *file, uint64_t hostptr, + uint64_t size, uint32_t flags); +void *msm_gem_kernel_new(struct drm_device *dev, uint32_t size, + uint32_t flags, struct msm_gem_address_space *aspace, + struct drm_gem_object **bo, uint64_t *iova); +void *msm_gem_kernel_new_locked(struct drm_device *dev, uint32_t size, + uint32_t flags, struct msm_gem_address_space *aspace, + struct drm_gem_object **bo, uint64_t *iova); int msm_framebuffer_prepare(struct drm_framebuffer *fb, struct msm_gem_address_space *aspace); void msm_framebuffer_cleanup(struct drm_framebuffer *fb, @@ -487,6 +527,19 @@ struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev, struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev); +struct msm_gpu_submitqueue; +int msm_submitqueue_init(struct msm_file_private *ctx); +struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx, + u32 id); +int msm_submitqueue_create(struct msm_file_private *ctx, u32 prio, + u32 flags, u32 *id); +int msm_submitqueue_query(struct msm_file_private *ctx, u32 id, u32 param, + void __user *data, u32 len); +int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id); +void msm_submitqueue_close(struct msm_file_private *ctx); + +void msm_submitqueue_destroy(struct kref *kref); + struct hdmi; int hdmi_modeset_init(struct hdmi *hdmi, struct drm_device *dev, struct drm_encoder *encoder); diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index cf127250c0d0..28b98cc1433c 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -104,10 +104,8 @@ static int msm_fbdev_create(struct drm_fb_helper *helper, /* allocate backing bo */ size = mode_cmd.pitches[0] * mode_cmd.height; DBG("allocating %d bytes for fb %d", size, dev->primary->index); - mutex_lock(&dev->struct_mutex); fbdev->bo = msm_gem_new(dev, size, MSM_BO_SCANOUT | MSM_BO_WC | MSM_BO_STOLEN); - mutex_unlock(&dev->struct_mutex); if (IS_ERR(fbdev->bo)) { ret = PTR_ERR(fbdev->bo); fbdev->bo = NULL; @@ -133,7 +131,7 @@ static int msm_fbdev_create(struct drm_fb_helper *helper, * in panic (ie. lock-safe, etc) we could avoid pinning the * buffer now: */ - ret = msm_gem_get_iova_locked(fbdev->bo, 0, &paddr); + ret = msm_gem_get_iova(fbdev->bo, 0, &paddr); if (ret) { dev_err(dev->dev, "failed to get buffer obj iova: %d\n", ret); goto fail_unlock; @@ -163,7 +161,7 @@ static int msm_fbdev_create(struct drm_fb_helper *helper, /* FIXME: Verify paddr < 32 bits? */ dev->mode_config.fb_base = lower_32_bits(paddr); - fbi->screen_base = msm_gem_vaddr_locked(fbdev->bo); + fbi->screen_base = msm_gem_vaddr(fbdev->bo); fbi->screen_size = fbdev->bo->size; fbi->fix.smem_start = lower_32_bits(paddr); fbi->fix.smem_len = fbdev->bo->size; diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 4bee797da746..d66071672c62 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -25,6 +25,129 @@ #include "msm_gpu.h" #include "msm_mmu.h" +static void msm_gem_mn_free(struct kref *refcount) +{ + struct msm_mmu_notifier *msm_mn = container_of(refcount, + struct msm_mmu_notifier, refcount); + + mmu_notifier_unregister(&msm_mn->mn, msm_mn->mm); + hash_del(&msm_mn->node); + + kfree(msm_mn); +} + +static int msm_gem_mn_get(struct msm_mmu_notifier *msm_mn) +{ + if (msm_mn) + return kref_get_unless_zero(&msm_mn->refcount); + return 0; +} + +static void msm_gem_mn_put(struct msm_mmu_notifier *msm_mn) +{ + if (msm_mn) { + struct msm_drm_private *msm_dev = msm_mn->msm_dev; + + mutex_lock(&msm_dev->mn_lock); + kref_put(&msm_mn->refcount, msm_gem_mn_free); + mutex_unlock(&msm_dev->mn_lock); + } +} + +void msm_mn_invalidate_range_start(struct mmu_notifier *mn, + struct mm_struct *mm, unsigned long start, unsigned long end); + +static const struct mmu_notifier_ops msm_mn_ops = { + .invalidate_range_start = msm_mn_invalidate_range_start, +}; + +static struct msm_mmu_notifier * +msm_gem_mn_find(struct msm_drm_private *msm_dev, struct mm_struct *mm, + struct msm_gem_address_space *aspace) +{ + struct msm_mmu_notifier *msm_mn; + int ret = 0; + + mutex_lock(&msm_dev->mn_lock); + hash_for_each_possible(msm_dev->mn_hash, msm_mn, node, + (unsigned long) mm) { + if (msm_mn->mm == mm) { + if (!msm_gem_mn_get(msm_mn)) { + ret = -EINVAL; + goto fail; + } + mutex_unlock(&msm_dev->mn_lock); + return msm_mn; + } + } + + msm_mn = kzalloc(sizeof(*msm_mn), GFP_KERNEL); + if (!msm_mn) { + ret = -ENOMEM; + goto fail; + } + + msm_mn->mm = current->mm; + msm_mn->mn.ops = &msm_mn_ops; + ret = mmu_notifier_register(&msm_mn->mn, msm_mn->mm); + if (ret) { + kfree(msm_mn); + goto fail; + } + + msm_mn->svm_tree = RB_ROOT; + spin_lock_init(&msm_mn->svm_tree_lock); + kref_init(&msm_mn->refcount); + msm_mn->msm_dev = msm_dev; + + /* Insert the msm_mn into the hash */ + hash_add(msm_dev->mn_hash, &msm_mn->node, (unsigned long) msm_mn->mm); + mutex_unlock(&msm_dev->mn_lock); + + return msm_mn; + +fail: + mutex_unlock(&msm_dev->mn_lock); + return ERR_PTR(ret); +} + +static int msm_gem_mn_register(struct msm_gem_svm_object *msm_svm_obj, + struct msm_gem_address_space *aspace) +{ + struct drm_gem_object *obj = &msm_svm_obj->msm_obj_base.base; + struct msm_drm_private *msm_dev = obj->dev->dev_private; + struct msm_mmu_notifier *msm_mn; + + msm_svm_obj->mm = current->mm; + msm_svm_obj->svm_node.start = msm_svm_obj->hostptr; + msm_svm_obj->svm_node.last = msm_svm_obj->hostptr + obj->size - 1; + + msm_mn = msm_gem_mn_find(msm_dev, msm_svm_obj->mm, aspace); + if (IS_ERR(msm_mn)) + return PTR_ERR(msm_mn); + + msm_svm_obj->msm_mn = msm_mn; + + spin_lock(&msm_mn->svm_tree_lock); + interval_tree_insert(&msm_svm_obj->svm_node, &msm_mn->svm_tree); + spin_unlock(&msm_mn->svm_tree_lock); + + return 0; +} + +static void msm_gem_mn_unregister(struct msm_gem_svm_object *msm_svm_obj) +{ + struct msm_mmu_notifier *msm_mn = msm_svm_obj->msm_mn; + + /* invalid: bo already unregistered */ + if (!msm_mn || msm_svm_obj->invalid) + return; + + spin_lock(&msm_mn->svm_tree_lock); + interval_tree_remove(&msm_svm_obj->svm_node, &msm_mn->svm_tree); + spin_unlock(&msm_mn->svm_tree_lock); +} + static int protect_pages(struct msm_gem_object *msm_obj) { int perm = PERM_READ | PERM_WRITE; @@ -63,8 +186,7 @@ static bool use_pages(struct drm_gem_object *obj) } /* allocate pages from VRAM carveout, used when no IOMMU: */ -static struct page **get_pages_vram(struct drm_gem_object *obj, - int npages) +static struct page **get_pages_vram(struct drm_gem_object *obj, int npages) { struct msm_gem_object *msm_obj = to_msm_bo(obj); struct msm_drm_private *priv = obj->dev->dev_private; @@ -76,8 +198,10 @@ static struct page **get_pages_vram(struct drm_gem_object *obj, if (!p) return ERR_PTR(-ENOMEM); + spin_lock(&priv->vram.lock); ret = drm_mm_insert_node(&priv->vram.mm, msm_obj->vram_node, npages, 0, DRM_MM_SEARCH_DEFAULT); + spin_unlock(&priv->vram.lock); if (ret) { drm_free_large(p); return ERR_PTR(ret); @@ -92,7 +216,6 @@ static struct page **get_pages_vram(struct drm_gem_object *obj, return p; } -/* called with dev->struct_mutex held */ static struct page **get_pages(struct drm_gem_object *obj) { struct msm_gem_object *msm_obj = to_msm_bo(obj); @@ -113,13 +236,15 @@ static struct page **get_pages(struct drm_gem_object *obj) return p; } + msm_obj->pages = p; + msm_obj->sgt = drm_prime_pages_to_sg(p, npages); if (IS_ERR(msm_obj->sgt)) { - dev_err(dev->dev, "failed to allocate sgt\n"); - return ERR_CAST(msm_obj->sgt); - } + void *ptr = ERR_CAST(msm_obj->sgt); - msm_obj->pages = p; + msm_obj->sgt = NULL; + return ptr; + } /* * Make sure to flush the CPU cache for newly allocated memory @@ -147,6 +272,18 @@ static struct page **get_pages(struct drm_gem_object *obj) return msm_obj->pages; } +static void put_pages_vram(struct drm_gem_object *obj) +{ + struct msm_gem_object *msm_obj = to_msm_bo(obj); + struct msm_drm_private *priv = obj->dev->dev_private; + + spin_lock(&priv->vram.lock); + drm_mm_remove_node(msm_obj->vram_node); + spin_unlock(&priv->vram.lock); + + drm_free_large(msm_obj->pages); +} + static void put_pages(struct drm_gem_object *obj) { struct msm_gem_object *msm_obj = to_msm_bo(obj); @@ -157,14 +294,22 @@ static void put_pages(struct drm_gem_object *obj) msm_obj->flags &= ~MSM_BO_LOCKED; } - sg_free_table(msm_obj->sgt); + if (msm_obj->sgt) + sg_free_table(msm_obj->sgt); kfree(msm_obj->sgt); if (use_pages(obj)) { - drm_gem_put_pages(obj, msm_obj->pages, true, false); + if (msm_obj->flags & MSM_BO_SVM) { + int npages = obj->size >> PAGE_SHIFT; + + release_pages(msm_obj->pages, npages, 0); + kfree(msm_obj->pages); + } else { + drm_gem_put_pages(obj, msm_obj->pages, + true, false); + } } else { - drm_mm_remove_node(msm_obj->vram_node); - drm_free_large(msm_obj->pages); + put_pages_vram(obj); } msm_obj->pages = NULL; @@ -173,11 +318,12 @@ static void put_pages(struct drm_gem_object *obj) struct page **msm_gem_get_pages(struct drm_gem_object *obj) { - struct drm_device *dev = obj->dev; + struct msm_gem_object *msm_obj = to_msm_bo(obj); struct page **p; - mutex_lock(&dev->struct_mutex); + + mutex_lock(&msm_obj->lock); p = get_pages(obj); - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&msm_obj->lock); return p; } @@ -191,8 +337,8 @@ int msm_gem_mmap_obj(struct drm_gem_object *obj, { struct msm_gem_object *msm_obj = to_msm_bo(obj); - /* We can't mmap secure objects */ - if (msm_obj->flags & MSM_BO_SECURE) { + /* We can't mmap secure objects or SVM objects */ + if (msm_obj->flags & (MSM_BO_SECURE | MSM_BO_SVM)) { drm_gem_vm_close(vma); return -EACCES; } @@ -237,16 +383,17 @@ int msm_gem_mmap(struct file *filp, struct vm_area_struct *vma) int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct drm_gem_object *obj = vma->vm_private_data; - struct drm_device *dev = obj->dev; + struct msm_gem_object *msm_obj = to_msm_bo(obj); struct page **pages; unsigned long pfn; pgoff_t pgoff; int ret; - /* Make sure we don't parallel update on a fault, nor move or remove - * something from beneath our feet + /* + * vm_ops.open and close get and put a reference on obj. + * So, we dont need to hold one here. */ - ret = mutex_lock_interruptible(&dev->struct_mutex); + ret = mutex_lock_interruptible(&msm_obj->lock); if (ret) goto out; @@ -269,7 +416,7 @@ int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); out_unlock: - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&msm_obj->lock); out: switch (ret) { case -EAGAIN: @@ -293,9 +440,10 @@ out: static uint64_t mmap_offset(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; + struct msm_gem_object *msm_obj = to_msm_bo(obj); int ret; - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + WARN_ON(!mutex_is_locked(&msm_obj->lock)); /* Make it mmapable */ ret = drm_gem_create_mmap_offset(obj); @@ -311,9 +459,11 @@ static uint64_t mmap_offset(struct drm_gem_object *obj) uint64_t msm_gem_mmap_offset(struct drm_gem_object *obj) { uint64_t offset; - mutex_lock(&obj->dev->struct_mutex); + struct msm_gem_object *msm_obj = to_msm_bo(obj); + + mutex_lock(&msm_obj->lock); offset = mmap_offset(obj); - mutex_unlock(&obj->dev->struct_mutex); + mutex_unlock(&msm_obj->lock); return offset; } @@ -325,19 +475,26 @@ static void obj_remove_domain(struct msm_gem_vma *domain) } } +/* Called with msm_obj->lock locked */ static void put_iova(struct drm_gem_object *obj) { - struct drm_device *dev = obj->dev; struct msm_gem_object *msm_obj = to_msm_bo(obj); + struct msm_gem_svm_object *msm_svm_obj; struct msm_gem_vma *domain, *tmp; + bool invalid = false; - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + WARN_ON(!mutex_is_locked(&msm_obj->lock)); + + if (msm_obj->flags & MSM_BO_SVM) { + msm_svm_obj = to_msm_svm_obj(msm_obj); + invalid = msm_svm_obj->invalid; + } list_for_each_entry_safe(domain, tmp, &msm_obj->domains, list) { if (iommu_present(&platform_bus_type)) { msm_gem_unmap_vma(domain->aspace, domain, - msm_obj->sgt, get_dmabuf_ptr(obj)); + msm_obj->sgt, get_dmabuf_ptr(obj), invalid); } obj_remove_domain(domain); @@ -378,14 +535,8 @@ static struct msm_gem_vma *obj_get_domain(struct drm_gem_object *obj, #define IOMMU_PRIV 0 #endif -/* should be called under struct_mutex.. although it can be called - * from atomic context without struct_mutex to acquire an extra - * iova ref if you know one is already held. - * - * That means when I do eventually need to add support for unpinning - * the refcnt counter needs to be atomic_t. - */ -int msm_gem_get_iova_locked(struct drm_gem_object *obj, +/* A reference to obj must be held before calling this function. */ +int msm_gem_get_iova(struct drm_gem_object *obj, struct msm_gem_address_space *aspace, uint64_t *iova) { struct msm_gem_object *msm_obj = to_msm_bo(obj); @@ -393,13 +544,18 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj, struct msm_gem_vma *domain; int ret = 0; + mutex_lock(&msm_obj->lock); + if (!iommu_present(&platform_bus_type)) { pages = get_pages(obj); - if (IS_ERR(pages)) + if (IS_ERR(pages)) { + mutex_unlock(&msm_obj->lock); return PTR_ERR(pages); + } *iova = (uint64_t) physaddr(obj); + mutex_unlock(&msm_obj->lock); return 0; } @@ -407,12 +563,15 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj, if (!domain) { domain = obj_add_domain(obj, aspace); - if (IS_ERR(domain)) + if (IS_ERR(domain)) { + mutex_unlock(&msm_obj->lock); return PTR_ERR(domain); + } pages = get_pages(obj); if (IS_ERR(pages)) { obj_remove_domain(domain); + mutex_unlock(&msm_obj->lock); return PTR_ERR(pages); } @@ -425,26 +584,8 @@ int msm_gem_get_iova_locked(struct drm_gem_object *obj, else obj_remove_domain(domain); - return ret; -} - -/* get iova, taking a reference. Should have a matching put */ -int msm_gem_get_iova(struct drm_gem_object *obj, - struct msm_gem_address_space *aspace, uint64_t *iova) -{ - struct msm_gem_vma *domain; - int ret; - - domain = obj_get_domain(obj, aspace); - if (domain) { - *iova = domain->iova; - return 0; - } - - mutex_lock(&obj->dev->struct_mutex); - ret = msm_gem_get_iova_locked(obj, aspace, iova); - mutex_unlock(&obj->dev->struct_mutex); - return ret; + mutex_unlock(&msm_obj->lock); + return 0; } /* get iova without taking a reference, used in places where you have @@ -453,11 +594,17 @@ int msm_gem_get_iova(struct drm_gem_object *obj, uint64_t msm_gem_iova(struct drm_gem_object *obj, struct msm_gem_address_space *aspace) { - struct msm_gem_vma *domain = obj_get_domain(obj, aspace); + struct msm_gem_object *msm_obj = to_msm_bo(obj); + struct msm_gem_vma *domain; + uint64_t iova; + mutex_lock(&msm_obj->lock); + domain = obj_get_domain(obj, aspace); WARN_ON(!domain); + iova = domain ? domain->iova : 0; + mutex_unlock(&msm_obj->lock); - return domain ? domain->iova : 0; + return iova; } void msm_gem_put_iova(struct drm_gem_object *obj, @@ -501,27 +648,31 @@ fail: return ret; } -void *msm_gem_vaddr_locked(struct drm_gem_object *obj) +void *msm_gem_vaddr(struct drm_gem_object *obj) { struct msm_gem_object *msm_obj = to_msm_bo(obj); - WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex)); - if (!msm_obj->vaddr) { + + mutex_lock(&msm_obj->lock); + + if (msm_obj->vaddr) { + mutex_unlock(&msm_obj->lock); + return msm_obj->vaddr; + } + + if (obj->import_attach) { + msm_obj->vaddr = dma_buf_vmap(obj->import_attach->dmabuf); + } else { struct page **pages = get_pages(obj); - if (IS_ERR(pages)) + if (IS_ERR(pages)) { + mutex_unlock(&msm_obj->lock); return ERR_CAST(pages); + } msm_obj->vaddr = vmap(pages, obj->size >> PAGE_SHIFT, VM_MAP, pgprot_writecombine(PAGE_KERNEL)); } - return msm_obj->vaddr; -} + mutex_unlock(&msm_obj->lock); -void *msm_gem_vaddr(struct drm_gem_object *obj) -{ - void *ret; - mutex_lock(&obj->dev->struct_mutex); - ret = msm_gem_vaddr_locked(obj); - mutex_unlock(&obj->dev->struct_mutex); - return ret; + return msm_obj->vaddr; } /* setup callback for when bo is no longer busy.. @@ -654,21 +805,32 @@ void msm_gem_free_object(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; struct msm_gem_object *msm_obj = to_msm_bo(obj); + struct msm_gem_svm_object *msm_svm_obj = NULL; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); /* object should not be on active list: */ WARN_ON(is_active(msm_obj)); + if (msm_obj->flags & MSM_BO_SVM) + msm_svm_obj = to_msm_svm_obj(msm_obj); + list_del(&msm_obj->mm_list); + /* Unregister SVM object from mmu notifications */ + if (msm_obj->flags & MSM_BO_SVM) { + msm_gem_mn_unregister(msm_svm_obj); + msm_gem_mn_put(msm_svm_obj->msm_mn); + msm_svm_obj->msm_mn = NULL; + } + + mutex_lock(&msm_obj->lock); put_iova(obj); if (obj->import_attach) { if (msm_obj->vaddr) dma_buf_vunmap(obj->import_attach->dmabuf, msm_obj->vaddr); - /* Don't drop the pages for imported dmabuf, as they are not * ours, just free the array we allocated: */ @@ -685,8 +847,12 @@ void msm_gem_free_object(struct drm_gem_object *obj) reservation_object_fini(msm_obj->resv); drm_gem_object_release(obj); + mutex_unlock(&msm_obj->lock); - kfree(msm_obj); + if (msm_obj->flags & MSM_BO_SVM) + kfree(msm_svm_obj); + else + kfree(msm_obj); } /* convenience method to construct a GEM buffer object, and userspace handle */ @@ -696,13 +862,28 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, struct drm_gem_object *obj; int ret; - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - obj = msm_gem_new(dev, size, flags); - mutex_unlock(&dev->struct_mutex); + if (IS_ERR(obj)) + return PTR_ERR(obj); + + ret = drm_gem_handle_create(file, obj, handle); + + /* drop reference from allocate - handle holds it now */ + drm_gem_object_unreference_unlocked(obj); + + return ret; +} + +/* convenience method to construct an SVM buffer object, and userspace handle */ +int msm_gem_svm_new_handle(struct drm_device *dev, struct drm_file *file, + uint64_t hostptr, uint64_t size, + uint32_t flags, uint32_t *handle) +{ + struct drm_gem_object *obj; + int ret; + + obj = msm_gem_svm_new(dev, file, hostptr, size, flags); if (IS_ERR(obj)) return PTR_ERR(obj); @@ -715,12 +896,11 @@ int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, return ret; } -static int msm_gem_new_impl(struct drm_device *dev, +static int msm_gem_obj_init(struct drm_device *dev, uint32_t size, uint32_t flags, - struct drm_gem_object **obj) + struct msm_gem_object *msm_obj, bool struct_mutex_locked) { struct msm_drm_private *priv = dev->dev_private; - struct msm_gem_object *msm_obj; bool use_vram = false; switch (flags & MSM_BO_CACHE_MASK) { @@ -742,9 +922,7 @@ static int msm_gem_new_impl(struct drm_device *dev, if (WARN_ON(use_vram && !priv->vram.size)) return -EINVAL; - msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL); - if (!msm_obj) - return -ENOMEM; + mutex_init(&msm_obj->lock); if (use_vram) { struct msm_gem_vma *domain = obj_add_domain(&msm_obj->base, 0); @@ -758,23 +936,45 @@ static int msm_gem_new_impl(struct drm_device *dev, msm_obj->resv = &msm_obj->_resv; reservation_object_init(msm_obj->resv); + INIT_LIST_HEAD(&msm_obj->mm_list); INIT_LIST_HEAD(&msm_obj->submit_entry); INIT_LIST_HEAD(&msm_obj->domains); - list_add_tail(&msm_obj->mm_list, &priv->inactive_list); - - *obj = &msm_obj->base; + if (struct_mutex_locked) { + list_add_tail(&msm_obj->mm_list, &priv->inactive_list); + } else { + mutex_lock(&dev->struct_mutex); + list_add_tail(&msm_obj->mm_list, &priv->inactive_list); + mutex_unlock(&dev->struct_mutex); + } return 0; } -struct drm_gem_object *msm_gem_new(struct drm_device *dev, - uint32_t size, uint32_t flags) +static struct drm_gem_object *msm_gem_new_impl(struct drm_device *dev, + uint32_t size, uint32_t flags, bool struct_mutex_locked) { - struct drm_gem_object *obj = NULL; + struct msm_gem_object *msm_obj; int ret; - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + msm_obj = kzalloc(sizeof(*msm_obj), GFP_KERNEL); + if (!msm_obj) + return ERR_PTR(-ENOMEM); + + ret = msm_gem_obj_init(dev, size, flags, msm_obj, struct_mutex_locked); + if (ret) { + kfree(msm_obj); + return ERR_PTR(ret); + } + + return &msm_obj->base; +} + +static struct drm_gem_object *_msm_gem_new(struct drm_device *dev, + uint32_t size, uint32_t flags, bool struct_mutex_locked) +{ + struct drm_gem_object *obj; + int ret; size = PAGE_ALIGN(size); @@ -785,9 +985,9 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, if (!size) return ERR_PTR(-EINVAL); - ret = msm_gem_new_impl(dev, size, flags, &obj); - if (ret) - goto fail; + obj = msm_gem_new_impl(dev, size, flags, struct_mutex_locked); + if (IS_ERR(obj)) + return obj; if (use_pages(obj)) { ret = drm_gem_object_init(dev, obj, size); @@ -800,8 +1000,160 @@ struct drm_gem_object *msm_gem_new(struct drm_device *dev, return obj; fail: - if (obj) - drm_gem_object_unreference(obj); + drm_gem_object_unreference_unlocked(obj); + + return ERR_PTR(ret); +} + +struct drm_gem_object *msm_gem_new_locked(struct drm_device *dev, + uint32_t size, uint32_t flags) +{ + return _msm_gem_new(dev, size, flags, true); +} + +struct drm_gem_object *msm_gem_new(struct drm_device *dev, + uint32_t size, uint32_t flags) +{ + return _msm_gem_new(dev, size, flags, false); +} + +static struct drm_gem_object *msm_svm_gem_new_impl(struct drm_device *dev, + uint32_t size, uint32_t flags) +{ + struct msm_gem_svm_object *msm_svm_obj; + struct msm_gem_object *msm_obj; + int ret; + + msm_svm_obj = kzalloc(sizeof(*msm_svm_obj), GFP_KERNEL); + if (!msm_svm_obj) + return ERR_PTR(-ENOMEM); + + msm_obj = &msm_svm_obj->msm_obj_base; + + ret = msm_gem_obj_init(dev, size, flags | MSM_BO_SVM, msm_obj, false); + if (ret) { + kfree(msm_svm_obj); + return ERR_PTR(ret); + } + + return &msm_obj->base; +} + +/* convenience method to construct an SVM GEM bo, and userspace handle */ +struct drm_gem_object *msm_gem_svm_new(struct drm_device *dev, + struct drm_file *file, uint64_t hostptr, + uint64_t size, uint32_t flags) +{ + struct drm_gem_object *obj; + struct msm_file_private *ctx = file->driver_priv; + struct msm_gem_address_space *aspace; + struct msm_gem_object *msm_obj; + struct msm_gem_svm_object *msm_svm_obj; + struct msm_gem_vma *domain = NULL; + struct page **p; + int npages; + int num_pinned = 0; + int write; + int ret; + + if (!ctx) + return ERR_PTR(-ENODEV); + + /* if we don't have IOMMU, don't bother pretending we can import: */ + if (!iommu_present(&platform_bus_type)) { + dev_err_once(dev->dev, "cannot import without IOMMU\n"); + return ERR_PTR(-EINVAL); + } + + /* hostptr and size must be page-aligned */ + if (offset_in_page(hostptr | size)) + return ERR_PTR(-EINVAL); + + /* Only CPU cached SVM objects are allowed */ + if ((flags & MSM_BO_CACHE_MASK) != MSM_BO_CACHED) + return ERR_PTR(-EINVAL); + + /* Allocate and initialize a new msm_gem_object */ + obj = msm_svm_gem_new_impl(dev, size, flags); + if (IS_ERR(obj)) + return obj; + + drm_gem_private_object_init(dev, obj, size); + + msm_obj = to_msm_bo(obj); + aspace = ctx->aspace; + domain = obj_add_domain(&msm_obj->base, aspace); + if (IS_ERR(domain)) { + drm_gem_object_unreference_unlocked(obj); + return ERR_CAST(domain); + } + + /* Reserve iova if not already in use, else fail */ + ret = msm_gem_reserve_iova(aspace, domain, hostptr, size); + if (ret) { + obj_remove_domain(domain); + drm_gem_object_unreference_unlocked(obj); + return ERR_PTR(ret); + } + + msm_svm_obj = to_msm_svm_obj(msm_obj); + msm_svm_obj->hostptr = hostptr; + msm_svm_obj->invalid = false; + + ret = msm_gem_mn_register(msm_svm_obj, aspace); + if (ret) + goto fail; + + /* + * Get physical pages and map into smmu in the ioctl itself. + * The driver handles iova allocation, physical page allocation and + * SMMU map all in one go. If we break this, then we have to maintain + * state to tell if physical pages allocation/map needs to happen. + * For SVM, iova reservation needs to happen in the ioctl itself, + * so do the rest right here as well. + */ + npages = size >> PAGE_SHIFT; + p = kcalloc(npages, sizeof(struct page *), GFP_KERNEL); + if (!p) { + ret = -ENOMEM; + goto fail; + } + + write = (msm_obj->flags & MSM_BO_GPU_READONLY) ? 0 : 1; + /* This may hold mm->mmap_sem */ + num_pinned = get_user_pages_fast(hostptr, npages, write, p); + if (num_pinned != npages) { + ret = -EINVAL; + goto free_pages; + } + + msm_obj->sgt = drm_prime_pages_to_sg(p, npages); + if (IS_ERR(msm_obj->sgt)) { + ret = PTR_ERR(msm_obj->sgt); + goto free_pages; + } + + msm_obj->pages = p; + + ret = aspace->mmu->funcs->map(aspace->mmu, domain->iova, + msm_obj->sgt, msm_obj->flags, get_dmabuf_ptr(obj)); + if (ret) + goto free_pages; + + kref_get(&aspace->kref); + + return obj; + +free_pages: + release_pages(p, num_pinned, 0); + kfree(p); + +fail: + if (domain) + msm_gem_release_iova(aspace, domain); + + obj_remove_domain(domain); + drm_gem_object_unreference_unlocked(obj); return ERR_PTR(ret); } @@ -821,21 +1173,20 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, size = PAGE_ALIGN(size); - mutex_lock(&dev->struct_mutex); - ret = msm_gem_new_impl(dev, size, MSM_BO_WC, &obj); - mutex_unlock(&dev->struct_mutex); - - if (ret) - goto fail; + obj = msm_gem_new_impl(dev, size, MSM_BO_WC, false); + if (IS_ERR(obj)) + return obj; drm_gem_private_object_init(dev, obj, size); npages = size / PAGE_SIZE; msm_obj = to_msm_bo(obj); + mutex_lock(&msm_obj->lock); msm_obj->sgt = sgt; msm_obj->pages = drm_malloc_ab(npages, sizeof(struct page *)); if (!msm_obj->pages) { + mutex_unlock(&msm_obj->lock); ret = -ENOMEM; goto fail; } @@ -843,15 +1194,146 @@ struct drm_gem_object *msm_gem_import(struct drm_device *dev, /* OR the passed in flags */ msm_obj->flags |= flags; - ret = drm_prime_sg_to_page_addr_arrays(sgt, msm_obj->pages, NULL, npages); - if (ret) + ret = drm_prime_sg_to_page_addr_arrays(sgt, msm_obj->pages, + NULL, npages); + if (ret) { + mutex_unlock(&msm_obj->lock); goto fail; + } + + mutex_unlock(&msm_obj->lock); return obj; fail: - if (obj) - drm_gem_object_unreference_unlocked(obj); + drm_gem_object_unreference_unlocked(obj); return ERR_PTR(ret); } + +/* Timeout in ms, long enough so we are sure the GPU is hung */ +#define SVM_OBJ_WAIT_TIMEOUT 10000 +static void invalidate_svm_object(struct msm_gem_svm_object *msm_svm_obj) +{ + struct msm_gem_object *msm_obj = &msm_svm_obj->msm_obj_base; + struct drm_device *dev = msm_obj->base.dev; + struct msm_gem_vma *domain, *tmp; + uint32_t fence; + int ret; + + if (is_active(msm_obj)) { + ktime_t timeout = ktime_add_ms(ktime_get(), + SVM_OBJ_WAIT_TIMEOUT); + + /* Get the most recent fence that touches the object */ + fence = msm_gem_fence(msm_obj, MSM_PREP_READ | MSM_PREP_WRITE); + + /* Wait for the fence to retire */ + ret = msm_wait_fence(dev, fence, &timeout, true); + if (ret) + /* The GPU could be hung! Not much we can do */ + dev_err(dev->dev, "drm: Error (%d) waiting for svm object: 0x%llx", + ret, msm_svm_obj->hostptr); + } + + /* GPU is done, unmap object from SMMU */ + mutex_lock(&msm_obj->lock); + list_for_each_entry_safe(domain, tmp, &msm_obj->domains, list) { + struct msm_gem_address_space *aspace = domain->aspace; + + if (domain->iova) + aspace->mmu->funcs->unmap(aspace->mmu, + domain->iova, msm_obj->sgt, + get_dmabuf_ptr(&msm_obj->base)); + } + /* Let go of the physical pages */ + put_pages(&msm_obj->base); + mutex_unlock(&msm_obj->lock); +} + +void msm_mn_invalidate_range_start(struct mmu_notifier *mn, + struct mm_struct *mm, unsigned long start, unsigned long end) +{ + struct msm_mmu_notifier *msm_mn = + container_of(mn, struct msm_mmu_notifier, mn); + struct interval_tree_node *itn = NULL; + struct msm_gem_svm_object *msm_svm_obj; + struct drm_gem_object *obj; + LIST_HEAD(inv_list); + + if (!msm_gem_mn_get(msm_mn)) + return; + + spin_lock(&msm_mn->svm_tree_lock); + itn = interval_tree_iter_first(&msm_mn->svm_tree, start, end - 1); + while (itn) { + msm_svm_obj = container_of(itn, + struct msm_gem_svm_object, svm_node); + obj = &msm_svm_obj->msm_obj_base.base; + + if (kref_get_unless_zero(&obj->refcount)) + list_add(&msm_svm_obj->lnode, &inv_list); + + itn = interval_tree_iter_next(itn, start, end - 1); + } + spin_unlock(&msm_mn->svm_tree_lock); + + list_for_each_entry(msm_svm_obj, &inv_list, lnode) { + obj = &msm_svm_obj->msm_obj_base.base; + /* Unregister SVM object from mmu notifications */ + msm_gem_mn_unregister(msm_svm_obj); + msm_svm_obj->invalid = true; + invalidate_svm_object(msm_svm_obj); + drm_gem_object_unreference_unlocked(obj); + } + + msm_gem_mn_put(msm_mn); +} + +/* + * Helper function to consolidate in-kernel buffer allocations that usually need + * to allocate a buffer object, iova and a virtual address all in one shot + */ +static void *_msm_gem_kernel_new(struct drm_device *dev, uint32_t size, + uint32_t flags, struct msm_gem_address_space *aspace, + struct drm_gem_object **bo, uint64_t *iova, bool locked) +{ + void *vaddr; + struct drm_gem_object *obj = _msm_gem_new(dev, size, flags, locked); + int ret; + + if (IS_ERR(obj)) + return ERR_CAST(obj); + + ret = msm_gem_get_iova(obj, aspace, iova); + if (ret) { + drm_gem_object_unreference(obj); + return ERR_PTR(ret); + } + + vaddr = msm_gem_vaddr(obj); + if (!vaddr) { + msm_gem_put_iova(obj, aspace); + drm_gem_object_unreference(obj); + return ERR_PTR(-ENOMEM); + } + + *bo = obj; + return vaddr; +} + +void *msm_gem_kernel_new(struct drm_device *dev, uint32_t size, + uint32_t flags, struct msm_gem_address_space *aspace, + struct drm_gem_object **bo, uint64_t *iova) +{ + return _msm_gem_kernel_new(dev, size, flags, aspace, bo, iova, + false); +} + +void *msm_gem_kernel_new_locked(struct drm_device *dev, uint32_t size, + uint32_t flags, struct msm_gem_address_space *aspace, + struct drm_gem_object **bo, uint64_t *iova) +{ + return _msm_gem_kernel_new(dev, size, flags, aspace, bo, iova, + true); +} diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index 41b4b5a4fd66..df9ddadc5c5c 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -20,17 +20,21 @@ #include <linux/kref.h> #include <linux/reservation.h> +#include <linux/mmu_notifier.h> +#include <linux/interval_tree.h> #include "msm_drv.h" /* Additional internal-use only BO flags: */ #define MSM_BO_STOLEN 0x10000000 /* try to use stolen/splash memory */ #define MSM_BO_LOCKED 0x20000000 /* Pages have been securely locked */ +#define MSM_BO_SVM 0x40000000 /* bo is SVM */ struct msm_gem_address_space { const char *name; struct msm_mmu *mmu; struct kref kref; struct drm_mm mm; + spinlock_t lock; /* Protects drm_mm node allocation/removal */ u64 va_len; }; @@ -80,9 +84,36 @@ struct msm_gem_object { * an IOMMU. Also used for stolen/splashscreen buffer. */ struct drm_mm_node *vram_node; + struct mutex lock; /* Protects resources associated with bo */ }; #define to_msm_bo(x) container_of(x, struct msm_gem_object, base) +struct msm_mmu_notifier { + struct mmu_notifier mn; + struct mm_struct *mm; /* mm_struct owning the mmu notifier mn */ + struct hlist_node node; + struct rb_root svm_tree; /* interval tree holding all svm bos */ + spinlock_t svm_tree_lock; /* Protects svm_tree*/ + struct msm_drm_private *msm_dev; + struct kref refcount; +}; + +struct msm_gem_svm_object { + struct msm_gem_object msm_obj_base; + uint64_t hostptr; + struct mm_struct *mm; /* mm_struct the svm bo belongs to */ + struct interval_tree_node svm_node; + struct msm_mmu_notifier *msm_mn; + struct list_head lnode; + /* bo has been unmapped on CPU, cannot be part of GPU submits */ + bool invalid; +}; + +#define to_msm_svm_obj(x) \ + ((struct msm_gem_svm_object *) \ + container_of(x, struct msm_gem_svm_object, msm_obj_base)) + + static inline bool is_active(struct msm_gem_object *msm_obj) { return msm_obj->gpu != NULL; @@ -101,6 +132,9 @@ static inline uint32_t msm_gem_fence(struct msm_gem_object *msm_obj, return fence; } +/* Internal submit flags */ +#define SUBMIT_FLAG_SKIP_HANGCHECK 0x00000001 + /* Created per submit-ioctl, to track bo's and cmdstream bufs, etc, * associated with the cmdstream submission for synchronization (and * make it easier to unwind when things go wrong, etc). This only @@ -109,15 +143,18 @@ static inline uint32_t msm_gem_fence(struct msm_gem_object *msm_obj, struct msm_gem_submit { struct drm_device *dev; struct msm_gem_address_space *aspace; - struct list_head node; /* node in gpu submit_list */ + struct list_head node; /* node in ring submit list */ struct list_head bo_list; struct ww_acquire_ctx ticket; uint32_t fence; int ring; + u32 flags; bool valid; uint64_t profile_buf_iova; - void *profile_buf_vaddr; + struct drm_msm_gem_submit_profile_buffer *profile_buf; bool secure; + struct msm_gpu_submitqueue *queue; + int tick_index; unsigned int nr_cmds; unsigned int nr_bos; struct { diff --git a/drivers/gpu/drm/msm/msm_gem_prime.c b/drivers/gpu/drm/msm/msm_gem_prime.c index 678018804f3a..9f3c097d011b 100644 --- a/drivers/gpu/drm/msm/msm_gem_prime.c +++ b/drivers/gpu/drm/msm/msm_gem_prime.c @@ -80,3 +80,10 @@ void msm_gem_prime_unpin(struct drm_gem_object *obj) if (!obj->import_attach) msm_gem_put_pages(obj); } + +struct reservation_object *msm_gem_prime_res_obj(struct drm_gem_object *obj) +{ + struct msm_gem_object *msm_obj = to_msm_bo(obj); + + return msm_obj->resv; +} diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 6d88eb374f69..b73379aa9ed7 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -18,6 +18,7 @@ #include "msm_drv.h" #include "msm_gpu.h" #include "msm_gem.h" +#include "msm_trace.h" /* * Cmdstream submission: @@ -35,7 +36,8 @@ static inline void __user *to_user_ptr(u64 address) static struct msm_gem_submit *submit_create(struct drm_device *dev, struct msm_gem_address_space *aspace, - uint32_t nr_bos, uint32_t nr_cmds) + uint32_t nr_bos, uint32_t nr_cmds, + struct msm_gpu_submitqueue *queue) { struct msm_gem_submit *submit; uint64_t sz = sizeof(*submit) + (nr_bos * sizeof(submit->bos[0])) + @@ -48,17 +50,23 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev, if (submit) { submit->dev = dev; submit->aspace = aspace; + submit->queue = queue; /* initially, until copy_from_user() and bo lookup succeeds: */ submit->nr_bos = 0; submit->nr_cmds = 0; - submit->profile_buf_vaddr = NULL; + submit->profile_buf = NULL; submit->profile_buf_iova = 0; submit->cmd = (void *)&submit->bos[nr_bos]; submit->secure = false; + /* + * Initalize node so we can safely list_del() on it if + * we fail in the submit path + */ + INIT_LIST_HEAD(&submit->node); INIT_LIST_HEAD(&submit->bo_list); ww_acquire_init(&submit->ticket, &reservation_ww_class); } @@ -74,6 +82,16 @@ copy_from_user_inatomic(void *to, const void __user *from, unsigned long n) return -EFAULT; } +void msm_gem_submit_free(struct msm_gem_submit *submit) +{ + if (!submit) + return; + + msm_submitqueue_put(submit->queue); + list_del(&submit->node); + kfree(submit); +} + static int submit_lookup_objects(struct msm_gpu *gpu, struct msm_gem_submit *submit, struct drm_msm_gem_submit *args, struct drm_file *file) @@ -217,9 +235,21 @@ retry: submit->bos[i].flags |= BO_LOCKED; } + /* + * An invalid SVM object is part of + * this submit's buffer list, fail. + */ + if (msm_obj->flags & MSM_BO_SVM) { + struct msm_gem_svm_object *msm_svm_obj = + to_msm_svm_obj(msm_obj); + if (msm_svm_obj->invalid) { + ret = -EINVAL; + goto fail; + } + } /* if locking succeeded, pin bo: */ - ret = msm_gem_get_iova_locked(&msm_obj->base, aspace, &iova); + ret = msm_gem_get_iova(&msm_obj->base, aspace, &iova); /* this would break the logic in the fail path.. there is no * reason for this to happen, but just to be on the safe side @@ -307,7 +337,7 @@ static int submit_reloc(struct msm_gem_submit *submit, struct msm_gem_object *ob /* For now, just map the entire thing. Eventually we probably * to do it page-by-page, w/ kmap() if not vmap()d.. */ - ptr = msm_gem_vaddr_locked(&obj->base); + ptr = msm_gem_vaddr(&obj->base); if (IS_ERR(ptr)) { ret = PTR_ERR(ptr); @@ -369,6 +399,9 @@ static void submit_cleanup(struct msm_gpu *gpu, struct msm_gem_submit *submit, { unsigned i; + if (!submit) + return; + for (i = 0; i < submit->nr_bos; i++) { struct msm_gem_object *msm_obj = submit->bos[i].obj; submit_unlock_unpin_bo(gpu, submit, i); @@ -386,6 +419,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, struct drm_msm_gem_submit *args = data; struct msm_file_private *ctx = file->driver_priv; struct msm_gem_submit *submit; + struct msm_gpu_submitqueue *queue; struct msm_gpu *gpu; unsigned i; int ret; @@ -397,12 +431,17 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, return -EINVAL; gpu = priv->gpu; - if (!gpu) + if (!gpu || !ctx) return -ENXIO; + queue = msm_submitqueue_get(ctx, args->queueid); + if (!queue) + return -ENOENT; + mutex_lock(&dev->struct_mutex); - submit = submit_create(dev, ctx->aspace, args->nr_bos, args->nr_cmds); + submit = submit_create(dev, ctx->aspace, args->nr_bos, args->nr_cmds, + queue); if (!submit) { ret = -ENOMEM; goto out; @@ -422,6 +461,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, to_user_ptr(args->cmds + (i * sizeof(submit_cmd))); struct msm_gem_object *msm_obj; uint64_t iova; + size_t size; ret = copy_from_user(&submit_cmd, userptr, sizeof(submit_cmd)); if (ret) { @@ -454,10 +494,12 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, goto out; } - if (!(submit_cmd.size) || - ((submit_cmd.size + submit_cmd.submit_offset) > - msm_obj->base.size)) { - DRM_ERROR("invalid cmdstream size: %u\n", submit_cmd.size); + size = submit_cmd.size + submit_cmd.submit_offset; + + if (!submit_cmd.size || (size < submit_cmd.size) || + (size > msm_obj->base.size)) { + DRM_ERROR("invalid cmdstream offset/size: %u/%u\n", + submit_cmd.submit_offset, submit_cmd.size); ret = -EINVAL; goto out; } @@ -469,8 +511,8 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, if (submit_cmd.type == MSM_SUBMIT_CMD_PROFILE_BUF) { submit->profile_buf_iova = submit->cmd[i].iova; - submit->profile_buf_vaddr = - msm_gem_vaddr_locked(&msm_obj->base); + submit->profile_buf = msm_gem_vaddr(&msm_obj->base) + + submit_cmd.submit_offset; } if (submit->valid) @@ -485,17 +527,16 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, submit->nr_cmds = i; /* Clamp the user submitted ring to the range of available rings */ - submit->ring = clamp_t(uint32_t, - (args->flags & MSM_SUBMIT_RING_MASK) >> MSM_SUBMIT_RING_SHIFT, - 0, gpu->nr_rings - 1); + submit->ring = clamp_t(uint32_t, queue->prio, 0, gpu->nr_rings - 1); ret = msm_gpu_submit(gpu, submit); args->fence = submit->fence; out: - if (submit) - submit_cleanup(gpu, submit, !!ret); + submit_cleanup(gpu, submit, !!ret); + if (ret) + msm_gem_submit_free(submit); mutex_unlock(&dev->struct_mutex); return ret; } diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index 47f7436854fb..f399d24019e4 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -52,6 +52,7 @@ msm_gem_address_space_new(struct msm_mmu *mmu, const char *name, if (!aspace) return ERR_PTR(-ENOMEM); + spin_lock_init(&aspace->lock); aspace->name = name; aspace->mmu = mmu; @@ -77,14 +78,19 @@ static int allocate_iova(struct msm_gem_address_space *aspace, if (!aspace->va_len) return 0; - if (WARN_ON(drm_mm_node_allocated(&vma->node))) - return 0; - for_each_sg(sgt->sgl, sg, sgt->nents, i) size += sg->length + sg->offset; - ret = drm_mm_insert_node(&aspace->mm, &vma->node, size >> PAGE_SHIFT, - 0, DRM_MM_SEARCH_DEFAULT); + spin_lock(&aspace->lock); + + if (WARN_ON(drm_mm_node_allocated(&vma->node))) { + spin_unlock(&aspace->lock); + return 0; + } + ret = drm_mm_insert_node(&aspace->mm, &vma->node, + size >> PAGE_SHIFT, 0, DRM_MM_SEARCH_BOTTOM_UP); + + spin_unlock(&aspace->lock); if (!ret && iova) *iova = vma->node.start << PAGE_SHIFT; @@ -92,6 +98,45 @@ static int allocate_iova(struct msm_gem_address_space *aspace, return ret; } +int msm_gem_reserve_iova(struct msm_gem_address_space *aspace, + struct msm_gem_vma *vma, + uint64_t hostptr, uint64_t size) +{ + struct drm_mm *mm = &aspace->mm; + uint64_t start = hostptr >> PAGE_SHIFT; + uint64_t last = (hostptr + size - 1) >> PAGE_SHIFT; + int ret; + + spin_lock(&aspace->lock); + + if (drm_mm_interval_first(mm, start, last)) { + /* iova already in use, fail */ + spin_unlock(&aspace->lock); + return -EADDRINUSE; + } + + vma->node.start = hostptr >> PAGE_SHIFT; + vma->node.size = size >> PAGE_SHIFT; + vma->node.color = 0; + + ret = drm_mm_reserve_node(mm, &vma->node); + if (!ret) + vma->iova = hostptr; + + spin_unlock(&aspace->lock); + + return ret; +} + +void msm_gem_release_iova(struct msm_gem_address_space *aspace, + struct msm_gem_vma *vma) +{ + spin_lock(&aspace->lock); + if (drm_mm_node_allocated(&vma->node)) + drm_mm_remove_node(&vma->node); + spin_unlock(&aspace->lock); +} + int msm_gem_map_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, struct sg_table *sgt, void *priv, unsigned int flags) @@ -110,9 +155,7 @@ int msm_gem_map_vma(struct msm_gem_address_space *aspace, flags, priv); if (ret) { - if (drm_mm_node_allocated(&vma->node)) - drm_mm_remove_node(&vma->node); - + msm_gem_release_iova(aspace, vma); return ret; } @@ -123,15 +166,16 @@ int msm_gem_map_vma(struct msm_gem_address_space *aspace, } void msm_gem_unmap_vma(struct msm_gem_address_space *aspace, - struct msm_gem_vma *vma, struct sg_table *sgt, void *priv) + struct msm_gem_vma *vma, struct sg_table *sgt, + void *priv, bool invalidated) { if (!aspace || !vma->iova) return; - aspace->mmu->funcs->unmap(aspace->mmu, vma->iova, sgt, priv); + if (!invalidated) + aspace->mmu->funcs->unmap(aspace->mmu, vma->iova, sgt, priv); - if (drm_mm_node_allocated(&vma->node)) - drm_mm_remove_node(&vma->node); + msm_gem_release_iova(aspace, vma); vma->iova = 0; diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index bef6e81de82a..6bac1cf6f7c5 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -18,7 +18,7 @@ #include "msm_gpu.h" #include "msm_gem.h" #include "msm_mmu.h" - +#include "msm_trace.h" /* * Power Management: @@ -274,19 +274,32 @@ static void inactive_start(struct msm_gpu *gpu) round_jiffies_up(jiffies + DRM_MSM_INACTIVE_JIFFIES)); } +static void retire_guilty_submit(struct msm_gpu *gpu, + struct msm_ringbuffer *ring) +{ + struct msm_gem_submit *submit = list_first_entry_or_null(&ring->submits, + struct msm_gem_submit, node); + + if (!submit) + return; + + submit->queue->faults++; + + msm_gem_submit_free(submit); +} + /* * Hangcheck detection for locked gpu: */ -static void retire_submits(struct msm_gpu *gpu, uint32_t fence); +static void retire_submits(struct msm_gpu *gpu, struct msm_ringbuffer *ring, + uint32_t fence); static void recover_worker(struct work_struct *work) { struct msm_gpu *gpu = container_of(work, struct msm_gpu, recover_work); struct drm_device *dev = gpu->dev; - dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name); - mutex_lock(&dev->struct_mutex); if (msm_gpu_active(gpu)) { struct msm_gem_submit *submit; @@ -295,30 +308,22 @@ static void recover_worker(struct work_struct *work) inactive_cancel(gpu); - FOR_EACH_RING(gpu, ring, i) { - uint32_t fence; + /* Retire all events that have already passed */ + FOR_EACH_RING(gpu, ring, i) + retire_submits(gpu, ring, + gpu->funcs->last_fence(gpu, ring)); - if (!ring) - continue; - - fence = gpu->funcs->last_fence(gpu, ring); - - /* - * Retire the faulting command on the active ring and - * make sure the other rings are cleaned up - */ - if (ring == gpu->funcs->active_ring(gpu)) - retire_submits(gpu, fence + 1); - else - retire_submits(gpu, fence); - } + retire_guilty_submit(gpu, gpu->funcs->active_ring(gpu)); /* Recover the GPU */ gpu->funcs->recover(gpu); - /* replay the remaining submits for all rings: */ - list_for_each_entry(submit, &gpu->submit_list, node) { - gpu->funcs->submit(gpu, submit); + /* Replay the remaining on all rings, highest priority first */ + for (i = gpu->nr_rings - 1; i >= 0; i--) { + struct msm_ringbuffer *ring = gpu->rb[i]; + + list_for_each_entry(submit, &ring->submits, node) + gpu->funcs->submit(gpu, submit); } } mutex_unlock(&dev->struct_mutex); @@ -346,8 +351,22 @@ static void hangcheck_handler(unsigned long data) /* some progress has been made.. ya! */ gpu->hangcheck_fence[ring->id] = fence; } else if (fence < submitted) { - /* no progress and not done.. hung! */ + struct msm_gem_submit *submit; + gpu->hangcheck_fence[ring->id] = fence; + + /* + * No progress done, but see if the current submit is + * intentionally skipping the hangcheck + */ + submit = list_first_entry_or_null(&ring->submits, + struct msm_gem_submit, node); + + if (!submit || (submit->queue->flags & + MSM_SUBMITQUEUE_BYPASS_QOS_TIMEOUT)) + goto out; + + /* no progress and not done and not special .. hung! */ dev_err(dev->dev, "%s: hangcheck detected gpu lockup rb %d!\n", gpu->name, ring->id); dev_err(dev->dev, "%s: completed fence: %u\n", @@ -358,6 +377,7 @@ static void hangcheck_handler(unsigned long data) queue_work(priv->wq, &gpu->recover_work); } +out: /* if still more pending work, reset the hangcheck timer: */ if (submitted > gpu->hangcheck_fence[ring->id]) hangcheck_timer_reset(gpu); @@ -465,23 +485,28 @@ out: * Cmdstream submission/retirement: */ -static void retire_submits(struct msm_gpu *gpu, uint32_t fence) +static void retire_submits(struct msm_gpu *gpu, struct msm_ringbuffer *ring, + uint32_t fence) { struct drm_device *dev = gpu->dev; struct msm_gem_submit *submit, *tmp; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - /* - * Find and retire all the submits in the same ring that are older than - * or equal to 'fence' - */ + list_for_each_entry_safe(submit, tmp, &ring->submits, node) { + struct msm_memptr_ticks *ticks; - list_for_each_entry_safe(submit, tmp, &gpu->submit_list, node) { - if (COMPARE_FENCE_LTE(submit->fence, fence)) { - list_del(&submit->node); - kfree(submit); - } + if (submit->fence > fence) + break; + + ticks = &(ring->memptrs->ticks[submit->tick_index]); + + /* Add memory barrier to ensure the timer ticks are posted */ + rmb(); + + trace_msm_retired(submit, ticks->started, ticks->retired); + + msm_gem_submit_free(submit); } } @@ -493,11 +518,12 @@ static bool _fence_signaled(struct msm_gem_object *obj, uint32_t fence) return COMPARE_FENCE_LTE(obj->read_fence, fence); } -static void _retire_ring(struct msm_gpu *gpu, uint32_t fence) +static void _retire_ring(struct msm_gpu *gpu, struct msm_ringbuffer *ring, + uint32_t fence) { struct msm_gem_object *obj, *tmp; - retire_submits(gpu, fence); + retire_submits(gpu, ring, fence); list_for_each_entry_safe(obj, tmp, &gpu->active_list, mm_list) { if (_fence_signaled(obj, fence)) { @@ -525,7 +551,7 @@ static void retire_worker(struct work_struct *work) msm_update_fence(gpu->dev, fence); mutex_lock(&dev->struct_mutex); - _retire_ring(gpu, fence); + _retire_ring(gpu, ring, fence); mutex_unlock(&dev->struct_mutex); } @@ -555,12 +581,18 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) inactive_cancel(gpu); - list_add_tail(&submit->node, &gpu->submit_list); + list_add_tail(&submit->node, &ring->submits); msm_rd_dump_submit(submit); ring->submitted_fence = submit->fence; + submit->tick_index = ring->tick_index; + ring->tick_index = (ring->tick_index + 1) % + ARRAY_SIZE(ring->memptrs->ticks); + + trace_msm_queued(submit); + update_sw_cntrs(gpu); for (i = 0; i < submit->nr_bos; i++) { @@ -580,8 +612,7 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) /* ring takes a reference to the bo and iova: */ drm_gem_object_reference(&msm_obj->base); - msm_gem_get_iova_locked(&msm_obj->base, - aspace, &iova); + msm_gem_get_iova(&msm_obj->base, aspace, &iova); } if (submit->bos[i].flags & MSM_SUBMIT_BO_READ) @@ -653,6 +684,9 @@ int msm_gpu_counter_put(struct msm_gpu *gpu, struct drm_msm_counter *data, { struct msm_context_counter *entry; + if (!gpu || !ctx) + return -ENODEV; + list_for_each_entry(entry, &ctx->counters, node) { if (entry->groupid == data->groupid && entry->counterid == data->counterid) { @@ -794,17 +828,39 @@ msm_gpu_create_address_space(struct msm_gpu *gpu, struct device *dev, gpu->name, name, PTR_ERR(aspace)); iommu_domain_free(iommu); - aspace = NULL; + return NULL; + } + + if (aspace->mmu) { + int ret = aspace->mmu->funcs->attach(aspace->mmu, NULL, 0); + + if (ret) { + dev_err(gpu->dev->dev, + "%s: failed to atach IOMMU '%s': %d\n", + gpu->name, name, ret); + msm_gem_address_space_put(aspace); + aspace = ERR_PTR(ret); + } } return aspace; } +static void msm_gpu_destroy_address_space(struct msm_gem_address_space *aspace) +{ + if (!IS_ERR_OR_NULL(aspace) && aspace->mmu) + aspace->mmu->funcs->detach(aspace->mmu); + + msm_gem_address_space_put(aspace); +} + int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs, const char *name, struct msm_gpu_config *config) { int i, ret, nr_rings; + void *memptrs; + uint64_t memptrs_iova; if (WARN_ON(gpu->num_perfcntrs > ARRAY_SIZE(gpu->last_cntrs))) gpu->num_perfcntrs = ARRAY_SIZE(gpu->last_cntrs); @@ -819,7 +875,6 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, INIT_WORK(&gpu->inactive_work, inactive_worker); INIT_WORK(&gpu->recover_work, recover_worker); - INIT_LIST_HEAD(&gpu->submit_list); setup_timer(&gpu->inactive_timer, inactive_handler, (unsigned long)gpu); @@ -888,12 +943,18 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, nr_rings = ARRAY_SIZE(gpu->rb); } + /* Allocate one buffer to hold all the memptr records for the rings */ + memptrs = msm_gem_kernel_new(drm, sizeof(struct msm_memptrs) * nr_rings, + MSM_BO_UNCACHED, gpu->aspace, &gpu->memptrs_bo, &memptrs_iova); + + if (IS_ERR(memptrs)) { + ret = PTR_ERR(memptrs); + goto fail; + } + /* Create ringbuffer(s): */ for (i = 0; i < nr_rings; i++) { - mutex_lock(&drm->struct_mutex); - gpu->rb[i] = msm_ringbuffer_new(gpu, i); - mutex_unlock(&drm->struct_mutex); - + gpu->rb[i] = msm_ringbuffer_new(gpu, i, memptrs, memptrs_iova); if (IS_ERR(gpu->rb[i])) { ret = PTR_ERR(gpu->rb[i]); gpu->rb[i] = NULL; @@ -901,6 +962,9 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, "could not create ringbuffer %d: %d\n", i, ret); goto fail; } + + memptrs += sizeof(struct msm_memptrs); + memptrs_iova += sizeof(struct msm_memptrs); } gpu->nr_rings = nr_rings; @@ -922,11 +986,17 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, return 0; fail: - for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) { - if (gpu->rb[i]) - msm_ringbuffer_destroy(gpu->rb[i]); + for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) + msm_ringbuffer_destroy(gpu->rb[i]); + + if (gpu->memptrs_bo) { + msm_gem_put_iova(gpu->memptrs_bo, gpu->aspace); + drm_gem_object_unreference_unlocked(gpu->memptrs_bo); } + msm_gpu_destroy_address_space(gpu->aspace); + msm_gpu_destroy_address_space(gpu->secure_aspace); + pm_runtime_disable(&pdev->dev); return ret; } @@ -944,16 +1014,17 @@ void msm_gpu_cleanup(struct msm_gpu *gpu) bs_fini(gpu); - for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) { - if (!gpu->rb[i]) - continue; - - if (gpu->rb[i]->iova) - msm_gem_put_iova(gpu->rb[i]->bo, gpu->aspace); - + for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) msm_ringbuffer_destroy(gpu->rb[i]); + + if (gpu->memptrs_bo) { + msm_gem_put_iova(gpu->memptrs_bo, gpu->aspace); + drm_gem_object_unreference_unlocked(gpu->memptrs_bo); } msm_snapshot_destroy(gpu, gpu->snapshot); pm_runtime_disable(&pdev->dev); + + msm_gpu_destroy_address_space(gpu->aspace); + msm_gpu_destroy_address_space(gpu->secure_aspace); } diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index a47eae68dd9b..306139bcd103 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -131,6 +131,8 @@ struct msm_gpu { struct pm_qos_request pm_qos_req_dma; + struct drm_gem_object *memptrs_bo; + #ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING struct msm_bus_scale_pdata *bus_scale_table; uint32_t bsc; @@ -147,12 +149,18 @@ struct msm_gpu { struct timer_list hangcheck_timer; uint32_t hangcheck_fence[MSM_GPU_MAX_RINGS]; struct work_struct recover_work; - - struct list_head submit_list; - struct msm_snapshot *snapshot; }; +struct msm_gpu_submitqueue { + int id; + u32 flags; + u32 prio; + int faults; + struct list_head node; + struct kref ref; +}; + /* It turns out that all targets use the same ringbuffer size. */ #define MSM_GPU_RINGBUFFER_SZ SZ_32K #define MSM_GPU_RINGBUFFER_BLKSIZE 32 @@ -280,4 +288,10 @@ void msm_gpu_cleanup_counters(struct msm_gpu *gpu, u64 msm_gpu_counter_read(struct msm_gpu *gpu, struct drm_msm_counter_read *data); +static inline void msm_submitqueue_put(struct msm_gpu_submitqueue *queue) +{ + if (queue) + kref_put(&queue->ref, msm_submitqueue_destroy); +} + #endif /* __MSM_GPU_H__ */ diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h index 033370ccbe24..8148d3e9e850 100644 --- a/drivers/gpu/drm/msm/msm_mmu.h +++ b/drivers/gpu/drm/msm/msm_mmu.h @@ -83,4 +83,8 @@ static inline void msm_mmu_disable(struct msm_mmu *mmu) mmu->funcs->disable(mmu); } +/* SDE smmu driver initialize and cleanup functions */ +int __init msm_smmu_driver_init(void); +void __exit msm_smmu_driver_cleanup(void); + #endif /* __MSM_MMU_H__ */ diff --git a/drivers/gpu/drm/msm/msm_rd.c b/drivers/gpu/drm/msm/msm_rd.c index 6dbb516cd4dc..edf3ff2a7a61 100644 --- a/drivers/gpu/drm/msm/msm_rd.c +++ b/drivers/gpu/drm/msm/msm_rd.c @@ -27,6 +27,11 @@ * This bypasses drm_debugfs_create_files() mainly because we need to use * our own fops for a bit more control. In particular, we don't want to * do anything if userspace doesn't have the debugfs file open. + * + * The module-param "rd_full", which defaults to false, enables snapshotting + * all (non-written) buffers in the submit, rather than just cmdstream bo's. + * This is useful to capture the contents of (for example) vbo's or textures, + * or shader programs (if not emitted inline in cmdstream). */ #ifdef CONFIG_DEBUG_FS @@ -40,6 +45,10 @@ #include "msm_gpu.h" #include "msm_gem.h" +static bool rd_full = false; +MODULE_PARM_DESC(rd_full, "If true, $debugfs/.../rd will snapshot all buffer contents"); +module_param_named(rd_full, rd_full, bool, 0600); + enum rd_sect_type { RD_NONE, RD_TEST, /* ascii text */ @@ -277,6 +286,36 @@ void msm_rd_debugfs_cleanup(struct drm_minor *minor) kfree(rd); } +static void snapshot_buf(struct msm_rd_state *rd, + struct msm_gem_submit *submit, int idx, + uint64_t iova, uint32_t size) +{ + struct msm_gem_object *obj = submit->bos[idx].obj; + uint64_t offset = 0; + + if (iova) { + offset = iova - submit->bos[idx].iova; + } else { + iova = submit->bos[idx].iova; + size = obj->base.size; + } + + /* Always write the RD_GPUADDR so we know how big the buffer is */ + rd_write_section(rd, RD_GPUADDR, + (uint64_t[2]) { iova, size }, 16); + + /* But only dump contents for buffers marked as read and not secure */ + if (submit->bos[idx].flags & MSM_SUBMIT_BO_READ && + !(obj->flags & MSM_BO_SECURE)) { + const char *buf = msm_gem_vaddr(&obj->base); + + if (IS_ERR_OR_NULL(buf)) + return; + + rd_write_section(rd, RD_BUFFER_CONTENTS, buf + offset, size); + } +} + /* called under struct_mutex */ void msm_rd_dump_submit(struct msm_gem_submit *submit) { @@ -300,24 +339,20 @@ void msm_rd_dump_submit(struct msm_gem_submit *submit) rd_write_section(rd, RD_CMD, msg, ALIGN(n, 4)); - /* could be nice to have an option (module-param?) to snapshot - * all the bo's associated with the submit. Handy to see vtx - * buffers, etc. For now just the cmdstream bo's is enough. - */ + if (rd_full) { + for (i = 0; i < submit->nr_bos; i++) + snapshot_buf(rd, submit, i, 0, 0); + } for (i = 0; i < submit->nr_cmds; i++) { - uint32_t idx = submit->cmd[i].idx; uint64_t iova = submit->cmd[i].iova; uint32_t szd = submit->cmd[i].size; /* in dwords */ - struct msm_gem_object *obj = submit->bos[idx].obj; - const char *buf = msm_gem_vaddr_locked(&obj->base); - - buf += iova - submit->bos[idx].iova; - rd_write_section(rd, RD_GPUADDR, - (uint64_t[2]) { iova, szd * 4 }, 16); - rd_write_section(rd, RD_BUFFER_CONTENTS, - buf, szd * 4); + /* snapshot cmdstream bo's (if we haven't already): */ + if (!rd_full) { + snapshot_buf(rd, submit, submit->cmd[i].idx, + submit->cmd[i].iova, szd * 4); + } switch (submit->cmd[i].type) { case MSM_SUBMIT_CMD_IB_TARGET_BUF: diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c index 14a16c4578d9..2a5843e6f81b 100644 --- a/drivers/gpu/drm/msm/msm_ringbuffer.c +++ b/drivers/gpu/drm/msm/msm_ringbuffer.c @@ -18,7 +18,8 @@ #include "msm_ringbuffer.h" #include "msm_gpu.h" -struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id) +struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id, + struct msm_memptrs *memptrs, uint64_t memptrs_iova) { struct msm_ringbuffer *ring; int ret; @@ -34,18 +35,24 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id) ring->gpu = gpu; ring->id = id; - ring->bo = msm_gem_new(gpu->dev, MSM_GPU_RINGBUFFER_SZ, MSM_BO_WC); + ring->bo = msm_gem_new(gpu->dev, MSM_GPU_RINGBUFFER_SZ, + MSM_BO_WC); if (IS_ERR(ring->bo)) { ret = PTR_ERR(ring->bo); ring->bo = NULL; goto fail; } - ring->start = msm_gem_vaddr_locked(ring->bo); + ring->memptrs = memptrs; + ring->memptrs_iova = memptrs_iova; + + + ring->start = msm_gem_vaddr(ring->bo); ring->end = ring->start + (MSM_GPU_RINGBUFFER_SZ >> 2); ring->next = ring->start; ring->cur = ring->start; + INIT_LIST_HEAD(&ring->submits); spin_lock_init(&ring->lock); return ring; @@ -58,7 +65,10 @@ fail: void msm_ringbuffer_destroy(struct msm_ringbuffer *ring) { - if (ring->bo) + if (ring && ring->bo) { + msm_gem_put_iova(ring->bo, ring->gpu->aspace); drm_gem_object_unreference_unlocked(ring->bo); + } + kfree(ring); } diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.h b/drivers/gpu/drm/msm/msm_ringbuffer.h index 1e84905073bf..b19ce75a4cc9 100644 --- a/drivers/gpu/drm/msm/msm_ringbuffer.h +++ b/drivers/gpu/drm/msm/msm_ringbuffer.h @@ -20,6 +20,27 @@ #include "msm_drv.h" +#define rbmemptr(ring, member) \ + ((ring)->memptrs_iova + offsetof(struct msm_memptrs, member)) + +struct msm_memptr_ticks { + uint64_t started; + uint64_t retired; +}; + +struct msm_memptrs { + volatile uint32_t rptr; + volatile uint32_t fence; + volatile uint64_t ttbr0; + volatile unsigned int contextidr; + struct msm_memptr_ticks ticks[128]; +}; + +#define RING_TICKS_IOVA(ring, index, field) \ + ((ring)->memptrs_iova + offsetof(struct msm_memptrs, ticks) + \ + ((index) * sizeof(struct msm_memptr_ticks)) + \ + offsetof(struct msm_memptr_ticks, field)) + struct msm_ringbuffer { struct msm_gpu *gpu; int id; @@ -28,9 +49,15 @@ struct msm_ringbuffer { uint64_t iova; uint32_t submitted_fence; spinlock_t lock; + struct list_head submits; + + struct msm_memptrs *memptrs; + uint64_t memptrs_iova; + int tick_index; }; -struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id); +struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id, + struct msm_memptrs *memptrs, uint64_t memptrs_iova); void msm_ringbuffer_destroy(struct msm_ringbuffer *ring); /* ringbuffer helpers (the parts that are same for a3xx/a2xx/z180..) */ diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c index c2dd5f96521e..eb68eb977aa7 100644 --- a/drivers/gpu/drm/msm/msm_smmu.c +++ b/drivers/gpu/drm/msm/msm_smmu.c @@ -120,16 +120,30 @@ static int msm_smmu_map(struct msm_mmu *mmu, uint64_t iova, { struct msm_smmu *smmu = to_msm_smmu(mmu); struct msm_smmu_client *client = msm_smmu_to_client(smmu); + struct iommu_domain *domain; int ret; - if (priv) - ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl, sgt->nents, - DMA_BIDIRECTIONAL, priv); - else - ret = dma_map_sg(client->dev, sgt->sgl, sgt->nents, - DMA_BIDIRECTIONAL); + if (!client || !sgt) + return -EINVAL; + + if (iova != 0) { + if (!client->mmu_mapping || !client->mmu_mapping->domain) + return -EINVAL; + + domain = client->mmu_mapping->domain; - return (ret != sgt->nents) ? -ENOMEM : 0; + return iommu_map_sg(domain, iova, sgt->sgl, + sgt->nents, flags); + } else { + if (priv) + ret = msm_dma_map_sg_lazy(client->dev, sgt->sgl, + sgt->nents, DMA_BIDIRECTIONAL, priv); + else + ret = dma_map_sg(client->dev, sgt->sgl, sgt->nents, + DMA_BIDIRECTIONAL); + + return (ret != sgt->nents) ? -ENOMEM : 0; + } } static void msm_smmu_unmap(struct msm_mmu *mmu, uint64_t iova, @@ -173,8 +187,8 @@ static struct msm_smmu_domain msm_smmu_domains[MSM_SMMU_DOMAIN_MAX] = { }, [MSM_SMMU_DOMAIN_SECURE] = { .label = "mdp_s", - .va_start = 0, - .va_size = SZ_4G, + .va_start = SZ_128K, + .va_size = SZ_4G - SZ_128K, .secure = true, }, [MSM_SMMU_DOMAIN_NRT_UNSECURE] = { @@ -185,20 +199,20 @@ static struct msm_smmu_domain msm_smmu_domains[MSM_SMMU_DOMAIN_MAX] = { }, [MSM_SMMU_DOMAIN_NRT_SECURE] = { .label = "rot_s", - .va_start = 0, - .va_size = SZ_4G, + .va_start = SZ_128K, + .va_size = SZ_4G - SZ_128K, .secure = true, }, }; static const struct of_device_id msm_smmu_dt_match[] = { - { .compatible = "qcom,smmu_mdp_unsec", + { .compatible = "qcom,smmu_sde_unsec", .data = &msm_smmu_domains[MSM_SMMU_DOMAIN_UNSECURE] }, - { .compatible = "qcom,smmu_mdp_sec", + { .compatible = "qcom,smmu_sde_sec", .data = &msm_smmu_domains[MSM_SMMU_DOMAIN_SECURE] }, - { .compatible = "qcom,smmu_rot_unsec", + { .compatible = "qcom,smmu_sde_nrt_unsec", .data = &msm_smmu_domains[MSM_SMMU_DOMAIN_NRT_UNSECURE] }, - { .compatible = "qcom,smmu_rot_sec", + { .compatible = "qcom,smmu_sde_nrt_sec", .data = &msm_smmu_domains[MSM_SMMU_DOMAIN_NRT_SECURE] }, {} }; @@ -394,7 +408,7 @@ static struct platform_driver msm_smmu_driver = { }, }; -static int __init msm_smmu_driver_init(void) +int __init msm_smmu_driver_init(void) { int ret; @@ -404,13 +418,11 @@ static int __init msm_smmu_driver_init(void) return ret; } -module_init(msm_smmu_driver_init); -static void __exit msm_smmu_driver_cleanup(void) +void __exit msm_smmu_driver_cleanup(void) { platform_driver_unregister(&msm_smmu_driver); } -module_exit(msm_smmu_driver_cleanup); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MSM SMMU driver"); diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c new file mode 100644 index 000000000000..4f2af876db49 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_submitqueue.c @@ -0,0 +1,151 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#include <linux/kref.h> +#include "msm_gpu.h" + +void msm_submitqueue_destroy(struct kref *kref) +{ + struct msm_gpu_submitqueue *queue = container_of(kref, + struct msm_gpu_submitqueue, ref); + + kfree(queue); +} + +struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx, + u32 id) +{ + struct msm_gpu_submitqueue *entry; + + if (!ctx) + return NULL; + + read_lock(&ctx->queuelock); + + list_for_each_entry(entry, &ctx->submitqueues, node) { + if (entry->id == id) { + kref_get(&entry->ref); + read_unlock(&ctx->queuelock); + + return entry; + } + } + + read_unlock(&ctx->queuelock); + return NULL; +} + +void msm_submitqueue_close(struct msm_file_private *ctx) +{ + struct msm_gpu_submitqueue *entry, *tmp; + + /* + * No lock needed in close and there won't + * be any more user ioctls coming our way + */ + + list_for_each_entry_safe(entry, tmp, &ctx->submitqueues, node) + msm_submitqueue_put(entry); +} + +int msm_submitqueue_create(struct msm_file_private *ctx, u32 prio, u32 flags, + u32 *id) +{ + struct msm_gpu_submitqueue *queue = kzalloc(sizeof(*queue), GFP_KERNEL); + + if (!queue) + return -ENOMEM; + + kref_init(&queue->ref); + queue->flags = flags; + queue->prio = prio; + + write_lock(&ctx->queuelock); + + queue->id = ctx->queueid++; + + if (id) + *id = queue->id; + + list_add_tail(&queue->node, &ctx->submitqueues); + + write_unlock(&ctx->queuelock); + + return 0; +} + +int msm_submitqueue_init(struct msm_file_private *ctx) +{ + INIT_LIST_HEAD(&ctx->submitqueues); + + rwlock_init(&ctx->queuelock); + + /* + * Add the "default" submitqueue with id 0 + * "medium" priority (3) and no flags + */ + + return msm_submitqueue_create(ctx, 3, 0, NULL); +} + +int msm_submitqueue_query(struct msm_file_private *ctx, u32 id, u32 param, + void __user *data, u32 len) +{ + struct msm_gpu_submitqueue *queue = msm_submitqueue_get(ctx, id); + int ret = 0; + + if (!queue) + return -ENOENT; + + if (param == MSM_SUBMITQUEUE_PARAM_FAULTS) { + u32 size = min_t(u32, len, sizeof(queue->faults)); + + if (copy_to_user(data, &queue->faults, size)) + ret = -EFAULT; + } else { + ret = -EINVAL; + } + + msm_submitqueue_put(queue); + + return ret; +} + +int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id) +{ + struct msm_gpu_submitqueue *entry; + + /* + * id 0 is the "default" queue and can't be destroyed + * by the user + */ + + if (!id) + return -ENOENT; + + write_lock(&ctx->queuelock); + + list_for_each_entry(entry, &ctx->submitqueues, node) { + if (entry->id == id) { + list_del(&entry->node); + write_unlock(&ctx->queuelock); + + msm_submitqueue_put(entry); + return 0; + } + } + + write_unlock(&ctx->queuelock); + return -ENOENT; +} + diff --git a/drivers/gpu/drm/msm/msm_trace.h b/drivers/gpu/drm/msm/msm_trace.h new file mode 100644 index 000000000000..68c7ff78ffc2 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_trace.h @@ -0,0 +1,98 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#if !defined(_MSM_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) +#define _MSM_TRACE_H_ + +#include <linux/tracepoint.h> + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM msm_drm +#define TRACE_INCLUDE_FILE msm_trace + +TRACE_EVENT(msm_queued, + TP_PROTO(struct msm_gem_submit *submit), + TP_ARGS(submit), + TP_STRUCT__entry( + __field(uint32_t, queue_id) + __field(uint32_t, fence_id) + __field(int, ring) + ), + TP_fast_assign( + __entry->queue_id = submit->queue->id; + __entry->fence_id = submit->fence; + __entry->ring = submit->ring; + ), + TP_printk( + "queue=%u fence=%u ring=%d", + __entry->queue_id, __entry->fence_id, __entry->ring + ) +); + +TRACE_EVENT(msm_submitted, + TP_PROTO(struct msm_gem_submit *submit, uint64_t ticks, uint64_t nsecs), + TP_ARGS(submit, ticks, nsecs), + TP_STRUCT__entry( + __field(uint32_t, queue_id) + __field(uint32_t, fence_id) + __field(int, ring) + __field(uint64_t, ticks) + __field(uint64_t, nsecs) + ), + TP_fast_assign( + __entry->queue_id = submit->queue->id; + __entry->fence_id = submit->fence; + __entry->ring = submit->ring; + __entry->ticks = ticks; + __entry->nsecs = nsecs; + ), + TP_printk( + "queue=%u fence=%u ring=%d ticks=%lld nsecs=%llu", + __entry->queue_id, __entry->fence_id, __entry->ring, + __entry->ticks, __entry->nsecs + ) +); + +TRACE_EVENT(msm_retired, + TP_PROTO(struct msm_gem_submit *submit, uint64_t start_ticks, + uint64_t retire_ticks), + TP_ARGS(submit, start_ticks, retire_ticks), + TP_STRUCT__entry( + __field(uint32_t, queue_id) + __field(uint32_t, fence_id) + __field(int, ring) + __field(uint64_t, start_ticks) + __field(uint64_t, retire_ticks) + ), + TP_fast_assign( + __entry->queue_id = submit->queue->id; + __entry->fence_id = submit->fence; + __entry->ring = submit->ring; + __entry->start_ticks = start_ticks; + __entry->retire_ticks = retire_ticks; + ), + TP_printk( + "queue=%u fence=%u ring=%d started=%lld retired=%lld", + __entry->queue_id, __entry->fence_id, __entry->ring, + __entry->start_ticks, __entry->retire_ticks + ) +); + + +#endif + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#include <trace/define_trace.h> + diff --git a/drivers/gpu/drm/msm/msm_trace_points.c b/drivers/gpu/drm/msm/msm_trace_points.c new file mode 100644 index 000000000000..41d9a975ac92 --- /dev/null +++ b/drivers/gpu/drm/msm/msm_trace_points.c @@ -0,0 +1,18 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#include "msm_gem.h" +#include "msm_gpu.h" + +#define CREATE_TRACE_POINTS +#include "msm_trace.h" diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index 7538927a4993..0bb8298c1013 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.c +++ b/drivers/gpu/drm/msm/sde/sde_connector.c @@ -17,6 +17,12 @@ #include "sde_connector.h" #include "sde_backlight.h" +#define SDE_DEBUG_CONN(c, fmt, ...) SDE_DEBUG("conn%d " fmt,\ + (c) ? (c)->base.base.id : -1, ##__VA_ARGS__) + +#define SDE_ERROR_CONN(c, fmt, ...) SDE_ERROR("conn%d " fmt,\ + (c) ? (c)->base.base.id : -1, ##__VA_ARGS__) + static const struct drm_prop_enum_list e_topology_name[] = { {SDE_RM_TOPOLOGY_UNKNOWN, "sde_unknown"}, {SDE_RM_TOPOLOGY_SINGLEPIPE, "sde_singlepipe"}, @@ -54,6 +60,36 @@ int sde_connector_get_info(struct drm_connector *connector, return c_conn->ops.get_info(info, c_conn->display); } +int sde_connector_pre_kickoff(struct drm_connector *connector) +{ + struct sde_connector *c_conn; + struct sde_connector_state *c_state; + struct msm_display_kickoff_params params; + int rc; + + if (!connector) { + SDE_ERROR("invalid argument\n"); + return -EINVAL; + } + + c_conn = to_sde_connector(connector); + c_state = to_sde_connector_state(connector->state); + + if (!c_conn->display) { + SDE_ERROR("invalid argument\n"); + return -EINVAL; + } + + if (!c_conn->ops.pre_kickoff) + return 0; + + params.hdr_metadata = &c_state->hdr_meta; + + rc = c_conn->ops.pre_kickoff(connector, c_conn->display, ¶ms); + + return rc; +} + static void sde_connector_destroy(struct drm_connector *connector) { struct sde_connector *c_conn; @@ -70,7 +106,8 @@ static void sde_connector_destroy(struct drm_connector *connector) if (c_conn->blob_caps) drm_property_unreference_blob(c_conn->blob_caps); - + if (c_conn->blob_hdr) + drm_property_unreference_blob(c_conn->blob_hdr); msm_property_destroy(&c_conn->property_info); drm_connector_unregister(connector); @@ -204,6 +241,68 @@ sde_connector_atomic_duplicate_state(struct drm_connector *connector) return &c_state->base; } +static int _sde_connector_set_hdr_info( + struct sde_connector *c_conn, + struct sde_connector_state *c_state, + void *usr_ptr) +{ + struct drm_connector *connector; + struct drm_msm_ext_panel_hdr_metadata *hdr_meta; + int i; + + if (!c_conn || !c_state) { + SDE_ERROR_CONN(c_conn, "invalid args\n"); + return -EINVAL; + } + + connector = &c_conn->base; + + if (!connector->hdr_supported) { + SDE_ERROR_CONN(c_conn, "sink doesn't support HDR\n"); + return -ENOTSUPP; + } + + memset(&c_state->hdr_meta, 0, sizeof(c_state->hdr_meta)); + + if (!usr_ptr) { + SDE_DEBUG_CONN(c_conn, "hdr metadata cleared\n"); + return 0; + } + + if (copy_from_user(&c_state->hdr_meta, + (void __user *)usr_ptr, + sizeof(*hdr_meta))) { + SDE_ERROR_CONN(c_conn, "failed to copy hdr metadata\n"); + return -EFAULT; + } + + hdr_meta = &c_state->hdr_meta; + + SDE_DEBUG_CONN(c_conn, "hdr_supported %d\n", + hdr_meta->hdr_supported); + SDE_DEBUG_CONN(c_conn, "eotf %d\n", + hdr_meta->eotf); + SDE_DEBUG_CONN(c_conn, "white_point_x %d\n", + hdr_meta->white_point_x); + SDE_DEBUG_CONN(c_conn, "white_point_y %d\n", + hdr_meta->white_point_y); + SDE_DEBUG_CONN(c_conn, "max_luminance %d\n", + hdr_meta->max_luminance); + SDE_DEBUG_CONN(c_conn, "max_content_light_level %d\n", + hdr_meta->max_content_light_level); + SDE_DEBUG_CONN(c_conn, "max_average_light_level %d\n", + hdr_meta->max_average_light_level); + + for (i = 0; i < HDR_PRIMARIES_COUNT; i++) { + SDE_DEBUG_CONN(c_conn, "display_primaries_x [%d]\n", + hdr_meta->display_primaries_x[i]); + SDE_DEBUG_CONN(c_conn, "display_primaries_y [%d]\n", + hdr_meta->display_primaries_y[i]); + } + + return 0; +} + static int sde_connector_atomic_set_property(struct drm_connector *connector, struct drm_connector_state *state, struct drm_property *property, @@ -263,6 +362,12 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, SDE_ERROR("invalid topology_control: 0x%llX\n", val); } + if (idx == CONNECTOR_PROP_HDR_METADATA) { + rc = _sde_connector_set_hdr_info(c_conn, c_state, (void *)val); + if (rc) + SDE_ERROR_CONN(c_conn, "cannot set hdr info %d\n", rc); + } + /* check for custom property handling */ if (!rc && c_conn->ops.set_property) { rc = c_conn->ops.set_property(connector, @@ -355,6 +460,32 @@ void sde_connector_complete_commit(struct drm_connector *connector) sde_fence_signal(&to_sde_connector(connector)->retire_fence, 0); } +static void sde_connector_update_hdr_props(struct drm_connector *connector) +{ + struct sde_connector *c_conn = to_sde_connector(connector); + struct drm_msm_ext_panel_hdr_properties hdr_prop = {}; + + hdr_prop.hdr_supported = connector->hdr_supported; + + if (hdr_prop.hdr_supported) { + hdr_prop.hdr_eotf = + connector->hdr_eotf; + hdr_prop.hdr_metadata_type_one = + connector->hdr_metadata_type_one; + hdr_prop.hdr_max_luminance = + connector->hdr_max_luminance; + hdr_prop.hdr_avg_luminance = + connector->hdr_avg_luminance; + hdr_prop.hdr_min_luminance = + connector->hdr_min_luminance; + } + msm_property_set_blob(&c_conn->property_info, + &c_conn->blob_hdr, + &hdr_prop, + sizeof(hdr_prop), + CONNECTOR_PROP_HDR_INFO); +} + static enum drm_connector_status sde_connector_detect(struct drm_connector *connector, bool force) { @@ -392,6 +523,7 @@ static const struct drm_connector_funcs sde_connector_ops = { static int sde_connector_get_modes(struct drm_connector *connector) { struct sde_connector *c_conn; + int ret = 0; if (!connector) { SDE_ERROR("invalid connector\n"); @@ -403,8 +535,11 @@ static int sde_connector_get_modes(struct drm_connector *connector) SDE_DEBUG("missing get_modes callback\n"); return 0; } + ret = c_conn->ops.get_modes(connector, c_conn->display); + if (ret) + sde_connector_update_hdr_props(connector); - return c_conn->ops.get_modes(connector, c_conn->display); + return ret; } static enum drm_mode_status @@ -575,6 +710,17 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, kfree(info); } + if (connector_type == DRM_MODE_CONNECTOR_HDMIA) { + msm_property_install_blob(&c_conn->property_info, + "hdr_properties", + DRM_MODE_PROP_IMMUTABLE, + CONNECTOR_PROP_HDR_INFO); + } + + msm_property_install_volatile_range(&c_conn->property_info, + "hdr_metadata", 0x0, 0, ~0, 0, + CONNECTOR_PROP_HDR_METADATA); + msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE", 0x0, 0, INR_OPEN_MAX, 0, CONNECTOR_PROP_RETIRE_FENCE); @@ -582,6 +728,10 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, "PLL_DELTA", 0x0, INT_MIN, INT_MAX, 0, CONNECTOR_PROP_PLL_DELTA); + msm_property_install_volatile_range(&c_conn->property_info, + "PLL_ENABLE", 0x0, 0, 1, 0, + CONNECTOR_PROP_PLL_ENABLE); + /* enum/bitmask properties */ msm_property_install_enum(&c_conn->property_info, "topology_name", DRM_MODE_PROP_IMMUTABLE, 0, e_topology_name, @@ -608,6 +758,8 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, error_destroy_property: if (c_conn->blob_caps) drm_property_unreference_blob(c_conn->blob_caps); + if (c_conn->blob_hdr) + drm_property_unreference_blob(c_conn->blob_hdr); msm_property_destroy(&c_conn->property_info); error_unregister_conn: drm_connector_unregister(&c_conn->base); diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h index 3f26ee7d5965..19e2b8a3e41c 100644 --- a/drivers/gpu/drm/msm/sde/sde_connector.h +++ b/drivers/gpu/drm/msm/sde/sde_connector.h @@ -122,6 +122,19 @@ struct sde_connector_ops { int (*get_info)(struct msm_display_info *info, void *display); int (*set_backlight)(void *display, u32 bl_lvl); + + + /** + * pre_kickoff - trigger display to program kickoff-time features + * @connector: Pointer to drm connector structure + * @display: Pointer to private display structure + * @params: Parameter bundle of connector-stored information for + * kickoff-time programming into the display + * Returns: Zero on success + */ + int (*pre_kickoff)(struct drm_connector *connector, + void *display, + struct msm_display_kickoff_params *params); }; /** @@ -139,6 +152,7 @@ struct sde_connector_ops { * @property_info: Private structure for generic property handling * @property_data: Array of private data for generic property handling * @blob_caps: Pointer to blob structure for 'capabilities' property + * @blob_hdr: Pointer to blob structure for 'hdr_properties' property */ struct sde_connector { struct drm_connector base; @@ -159,6 +173,7 @@ struct sde_connector { struct msm_property_info property_info; struct msm_property_data property_data[CONNECTOR_PROP_COUNT]; struct drm_property_blob *blob_caps; + struct drm_property_blob *blob_hdr; }; /** @@ -206,12 +221,14 @@ struct sde_connector { * @out_fb: Pointer to output frame buffer, if applicable * @aspace: Address space for accessing frame buffer objects, if applicable * @property_values: Local cache of current connector property values + * @hdr_meta: HDR metadata info passed from userspace */ struct sde_connector_state { struct drm_connector_state base; struct drm_framebuffer *out_fb; struct msm_gem_address_space *aspace; uint64_t property_values[CONNECTOR_PROP_COUNT]; + struct drm_msm_ext_panel_hdr_metadata hdr_meta; }; /** @@ -303,5 +320,12 @@ void sde_connector_complete_commit(struct drm_connector *connector); int sde_connector_get_info(struct drm_connector *connector, struct msm_display_info *info); +/** + * sde_connector_pre_kickoff - trigger kickoff time feature programming + * @connector: Pointer to drm connector object + * Returns: Zero on success + */ +int sde_connector_pre_kickoff(struct drm_connector *connector); + #endif /* _SDE_CONNECTOR_H_ */ diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index adfe9e54e6f4..7d2291dadc18 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -36,6 +36,7 @@ #include "sde_connector.h" #include "sde_power_handle.h" #include "sde_core_perf.h" +#include "sde_trace.h" /* default input fence timeout, in ms */ #define SDE_CRTC_INPUT_FENCE_TIMEOUT 2000 @@ -57,6 +58,7 @@ static inline struct sde_kms *_sde_crtc_get_kms(struct drm_crtc *crtc) { struct msm_drm_private *priv = crtc->dev->dev_private; + return to_sde_kms(priv->kms); } @@ -183,9 +185,6 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc, pstate = to_sde_plane_state(plane->state); - flush_mask = ctl->ops.get_bitmask_sspp(ctl, - sde_plane_pipe(plane)); - /* always stage plane on either left or right lm */ if (plane->state->crtc_x >= crtc_split_width) { lm_idx = RIGHT_MIXER; @@ -195,20 +194,36 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc, idx = left_crtc_zpos_cnt[pstate->stage]++; } + /* + * program each mixer with two hw pipes in dual mixer mode, + */ + if (sde_crtc->num_mixers == CRTC_DUAL_MIXERS) { + stage_cfg->stage[LEFT_MIXER][pstate->stage][1] = + sde_plane_pipe(plane, 1); + + flush_mask = ctl->ops.get_bitmask_sspp(ctl, + sde_plane_pipe(plane, 1)); + } + + flush_mask |= ctl->ops.get_bitmask_sspp(ctl, + sde_plane_pipe(plane, lm_idx ? 1 : 0)); + /* stage plane on right LM if it crosses the boundary */ lm_right = (lm_idx == LEFT_MIXER) && (plane->state->crtc_x + plane->state->crtc_w > crtc_split_width); stage_cfg->stage[lm_idx][pstate->stage][idx] = - sde_plane_pipe(plane); + sde_plane_pipe(plane, lm_idx ? 1 : 0); + mixer[lm_idx].flush_mask |= flush_mask; SDE_DEBUG("crtc %d stage:%d - plane %d sspp %d fb %d\n", crtc->base.id, pstate->stage, plane->base.id, - sde_plane_pipe(plane) - SSPP_VIG0, + sde_plane_pipe(plane, + lm_idx ? 1 : 0) - SSPP_VIG0, plane->state->fb ? plane->state->fb->base.id : -1); @@ -230,8 +245,19 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc, if (lm_right) { idx = right_crtc_zpos_cnt[pstate->stage]++; - stage_cfg->stage[RIGHT_MIXER][pstate->stage][idx] = - sde_plane_pipe(plane); + + /* + * program each mixer with two hw pipes + in dual mixer mode, + */ + if (sde_crtc->num_mixers == CRTC_DUAL_MIXERS) { + stage_cfg->stage[RIGHT_MIXER][pstate->stage][1] + = sde_plane_pipe(plane, 0); + } + + stage_cfg->stage[RIGHT_MIXER][pstate->stage][idx] + = sde_plane_pipe(plane, 1); + mixer[RIGHT_MIXER].flush_mask |= flush_mask; /* blend config update */ @@ -342,12 +368,6 @@ void sde_crtc_prepare_commit(struct drm_crtc *crtc, cstate->is_rt = true; } - if (cstate->num_connectors > 0 && cstate->connectors[0]->encoder) - cstate->intf_mode = sde_encoder_get_intf_mode( - cstate->connectors[0]->encoder); - else - cstate->intf_mode = INTF_MODE_NONE; - /* prepare main output fence */ sde_fence_prepare(&sde_crtc->output_fence); } @@ -398,6 +418,22 @@ static void _sde_crtc_complete_flip(struct drm_crtc *crtc, spin_unlock_irqrestore(&dev->event_lock, flags); } +enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc) +{ + struct drm_encoder *encoder; + + if (!crtc || !crtc->dev) { + SDE_ERROR("invalid crtc\n"); + return INTF_MODE_NONE; + } + + drm_for_each_encoder(encoder, crtc->dev) + if (encoder->crtc == crtc) + return sde_encoder_get_intf_mode(encoder); + + return INTF_MODE_NONE; +} + static void sde_crtc_vblank_cb(void *data) { struct drm_crtc *crtc = (struct drm_crtc *)data; @@ -617,6 +653,7 @@ static void _sde_crtc_wait_for_fences(struct drm_crtc *crtc) * that each plane can check its fence status and react appropriately * if its fence has timed out. */ + SDE_ATRACE_BEGIN("plane_wait_input_fence"); drm_atomic_crtc_for_each_plane(plane, crtc) { if (wait_ms) { /* determine updated wait time */ @@ -628,6 +665,7 @@ static void _sde_crtc_wait_for_fences(struct drm_crtc *crtc) } sde_plane_wait_input_fence(plane, wait_ms); } + SDE_ATRACE_END("plane_wait_input_fence"); } static void _sde_crtc_setup_mixer_for_encoder( @@ -862,6 +900,7 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc) sde_kms = _sde_crtc_get_kms(crtc); priv = sde_kms->dev->dev_private; + SDE_ATRACE_BEGIN("crtc_commit"); list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (encoder->crtc != crtc) continue; @@ -878,7 +917,7 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc) SDE_ERROR("crtc%d invalid frame pending\n", crtc->base.id); SDE_EVT32(DRMID(crtc), 0); - return; + goto end; } else if (atomic_inc_return(&sde_crtc->frame_pending) == 1) { /* acquire bandwidth and other resources */ SDE_DEBUG("crtc%d first commit\n", crtc->base.id); @@ -896,6 +935,9 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc) sde_encoder_kickoff(encoder); } +end: + SDE_ATRACE_END("crtc_commit"); + return; } /** @@ -1256,7 +1298,8 @@ int sde_crtc_vblank(struct drm_crtc *crtc, bool en) return 0; } -void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file) +void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, + struct drm_file *file) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); @@ -1275,6 +1318,10 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, struct drm_device *dev; struct sde_kms_info *info; struct sde_kms *sde_kms; + static const struct drm_prop_enum_list e_secure_level[] = { + {SDE_DRM_SEC_NON_SEC, "sec_and_non_sec"}, + {SDE_DRM_SEC_ONLY, "sec_only"}, + }; SDE_DEBUG("\n"); @@ -1320,6 +1367,12 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, msm_property_install_blob(&sde_crtc->property_info, "capabilities", DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_INFO); + + msm_property_install_enum(&sde_crtc->property_info, "security_level", + 0x0, 0, e_secure_level, + ARRAY_SIZE(e_secure_level), + CRTC_PROP_SECURITY_LEVEL); + sde_kms_info_reset(info); sde_kms_info_add_keyint(info, "hw_version", catalog->hwversion); @@ -1622,7 +1675,7 @@ static int sde_crtc_debugfs_state_show(struct seq_file *s, void *v) seq_printf(s, "num_connectors: %d\n", cstate->num_connectors); seq_printf(s, "is_rt: %d\n", cstate->is_rt); - seq_printf(s, "intf_mode: %d\n", cstate->intf_mode); + seq_printf(s, "intf_mode: %d\n", sde_crtc_get_intf_mode(crtc)); seq_printf(s, "bw_ctl: %llu\n", cstate->cur_perf.bw_ctl); seq_printf(s, "core_clk_rate: %u\n", cstate->cur_perf.core_clk_rate); seq_printf(s, "max_per_pipe_ib: %llu\n", @@ -1665,7 +1718,8 @@ static void _sde_crtc_init_debugfs(struct sde_crtc *sde_crtc, #endif /* initialize crtc */ -struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane) +struct drm_crtc *sde_crtc_init(struct drm_device *dev, + struct drm_plane *plane) { struct drm_crtc *crtc = NULL; struct sde_crtc *sde_crtc = NULL; diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h index 97a20b987ef5..aaa815c76c4e 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.h +++ b/drivers/gpu/drm/msm/sde/sde_crtc.h @@ -246,16 +246,10 @@ void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file); bool sde_crtc_is_rt(struct drm_crtc *crtc); /** - * sde_crtc_get_intf_mode - get interface mode of the given crtc + * sde_crtc_get_intf_mode - get primary interface mode of the given crtc * @crtc: Pointert to crtc */ -static inline enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc) -{ - struct sde_crtc_state *cstate = - crtc ? to_sde_crtc_state(crtc->state) : NULL; - - return cstate ? cstate->intf_mode : INTF_MODE_NONE; -} +enum sde_intf_mode sde_crtc_get_intf_mode(struct drm_crtc *crtc); /** * sde_core_perf_crtc_is_wb - check if writeback is primary output of this crtc diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index 030b192e5df4..29444a83cf02 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -32,6 +32,7 @@ #include "sde_formats.h" #include "sde_encoder_phys.h" #include "sde_color_processing.h" +#include "sde_trace.h" #define SDE_DEBUG_ENC(e, fmt, ...) SDE_DEBUG("enc%d " fmt,\ (e) ? (e)->base.base.id : -1, ##__VA_ARGS__) @@ -514,6 +515,7 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc, if (!drm_enc || !phy_enc) return; + SDE_ATRACE_BEGIN("encoder_vblank_callback"); sde_enc = to_sde_encoder_virt(drm_enc); spin_lock_irqsave(&sde_enc->enc_spinlock, lock_flags); @@ -522,6 +524,7 @@ static void sde_encoder_vblank_callback(struct drm_encoder *drm_enc, spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags); atomic_inc(&phy_enc->vsync_cnt); + SDE_ATRACE_END("encoder_vblank_callback"); } static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc, @@ -530,8 +533,10 @@ static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc, if (!phy_enc) return; + SDE_ATRACE_BEGIN("encoder_underrun_callback"); atomic_inc(&phy_enc->underrun_cnt); SDE_EVT32(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt)); + SDE_ATRACE_END("encoder_underrun_callback"); } void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc, @@ -795,6 +800,7 @@ void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc) struct sde_encoder_virt *sde_enc; struct sde_encoder_phys *phys; unsigned int i; + int rc; if (!drm_enc) { SDE_ERROR("invalid encoder\n"); @@ -811,6 +817,14 @@ void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc) if (phys && phys->ops.prepare_for_kickoff) phys->ops.prepare_for_kickoff(phys); } + + if (sde_enc->cur_master && sde_enc->cur_master->connector) { + rc = sde_connector_pre_kickoff(sde_enc->cur_master->connector); + if (rc) + SDE_ERROR_ENC(sde_enc, "kickoff conn%d failed rc %d\n", + sde_enc->cur_master->connector->base.id, + rc); + } } void sde_encoder_kickoff(struct drm_encoder *drm_enc) @@ -823,6 +837,7 @@ void sde_encoder_kickoff(struct drm_encoder *drm_enc) SDE_ERROR("invalid encoder\n"); return; } + SDE_ATRACE_BEGIN("encoder_kickoff"); sde_enc = to_sde_encoder_virt(drm_enc); SDE_DEBUG_ENC(sde_enc, "\n"); @@ -842,6 +857,7 @@ void sde_encoder_kickoff(struct drm_encoder *drm_enc) if (phys && phys->ops.handle_post_kickoff) phys->ops.handle_post_kickoff(phys); } + SDE_ATRACE_END("encoder_kickoff"); } static int _sde_encoder_status_show(struct seq_file *s, void *data) diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c index 9368c4974126..a48962a2384b 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c @@ -28,6 +28,24 @@ #define WBID(wb_enc) ((wb_enc) ? wb_enc->wb_dev->wb_idx : -1) +#define TO_S15D16(_x_) ((_x_) << 7) + +/** + * sde_rgb2yuv_601l - rgb to yuv color space conversion matrix + * + */ +static struct sde_csc_cfg sde_encoder_phys_wb_rgb2yuv_601l = { + { + TO_S15D16(0x0083), TO_S15D16(0x0102), TO_S15D16(0x0032), + TO_S15D16(0x1fb5), TO_S15D16(0x1f6c), TO_S15D16(0x00e1), + TO_S15D16(0x00e1), TO_S15D16(0x1f45), TO_S15D16(0x1fdc) + }, + { 0x00, 0x00, 0x00 }, + { 0x0040, 0x0200, 0x0200 }, + { 0x000, 0x3ff, 0x000, 0x3ff, 0x000, 0x3ff }, + { 0x040, 0x3ac, 0x040, 0x3c0, 0x040, 0x3c0 }, +}; + /** * sde_encoder_phys_wb_is_master - report wb always as master encoder */ @@ -150,6 +168,15 @@ void sde_encoder_phys_setup_cdm(struct sde_encoder_phys *phys_enc, cdm_cfg->h_cdwn_type, cdm_cfg->v_cdwn_type); + if (hw_cdm && hw_cdm->ops.setup_csc_data) { + ret = hw_cdm->ops.setup_csc_data(hw_cdm, + &sde_encoder_phys_wb_rgb2yuv_601l); + if (ret < 0) { + SDE_ERROR("failed to setup CSC %d\n", ret); + return; + } + } + if (hw_cdm && hw_cdm->ops.setup_cdwn) { ret = hw_cdm->ops.setup_cdwn(hw_cdm, cdm_cfg); if (ret < 0) { diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c index a59ec31ba276..2187d221a352 100644 --- a/drivers/gpu/drm/msm/sde/sde_formats.c +++ b/drivers/gpu/drm/msm/sde/sde_formats.c @@ -10,13 +10,18 @@ * GNU General Public License for more details. */ +#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ + #include <uapi/drm/drm_fourcc.h> +#include <uapi/media/msm_media_info.h> #include "sde_kms.h" #include "sde_formats.h" #define SDE_UBWC_META_MACRO_W_H 16 #define SDE_UBWC_META_BLOCK_SIZE 256 +#define SDE_UBWC_PLANE_SIZE_ALIGNMENT 4096 + #define SDE_MAX_IMG_WIDTH 0x3FFF #define SDE_MAX_IMG_HEIGHT 0x3FFF @@ -42,7 +47,7 @@ bp, flg, fm, np) \ .unpack_count = uc, \ .bpp = bp, \ .fetch_mode = fm, \ - .flag = flg, \ + .flag = {(flg)}, \ .num_planes = np \ } @@ -60,7 +65,7 @@ alpha, chroma, count, bp, flg, fm, np) \ .unpack_count = count, \ .bpp = bp, \ .fetch_mode = fm, \ - .flag = flg, \ + .flag = {(flg)}, \ .num_planes = np \ } @@ -77,7 +82,24 @@ alpha, chroma, count, bp, flg, fm, np) \ .unpack_count = 2, \ .bpp = 2, \ .fetch_mode = fm, \ - .flag = flg, \ + .flag = {(flg)}, \ + .num_planes = np \ +} + +#define PSEUDO_YUV_FMT_LOOSE(fmt, a, r, g, b, e0, e1, chroma, flg, fm, np)\ +{ \ + .base.pixel_format = DRM_FORMAT_ ## fmt, \ + .fetch_planes = SDE_PLANE_PSEUDO_PLANAR, \ + .alpha_enable = false, \ + .element = { (e0), (e1), 0, 0 }, \ + .bits = { g, b, r, a }, \ + .chroma_sample = chroma, \ + .unpack_align_msb = 1, \ + .unpack_tight = 0, \ + .unpack_count = 2, \ + .bpp = 2, \ + .fetch_mode = fm, \ + .flag = {(flg)}, \ .num_planes = np \ } @@ -95,10 +117,20 @@ flg, fm, np) \ .unpack_count = 1, \ .bpp = bp, \ .fetch_mode = fm, \ - .flag = flg, \ + .flag = {(flg)}, \ .num_planes = np \ } +/* + * struct sde_media_color_map - maps drm format to media format + * @format: DRM base pixel format + * @color: Media API color related to DRM format + */ +struct sde_media_color_map { + uint32_t format; + uint32_t color; +}; + static const struct sde_format sde_format_map[] = { INTERLEAVED_RGB_FMT(ARGB8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, @@ -270,49 +302,49 @@ static const struct sde_format sde_format_map[] = { INTERLEAVED_RGB_FMT(BGRA1010102, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, true, 4, SDE_FORMAT_FLAG_DX, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBA1010102, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, true, 4, SDE_FORMAT_FLAG_DX, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ABGR2101010, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 4, SDE_FORMAT_FLAG_DX, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(ARGB2101010, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, true, 4, SDE_FORMAT_FLAG_DX, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XRGB2101010, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, false, 4, SDE_FORMAT_FLAG_DX, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(BGRX1010102, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, false, 4, SDE_FORMAT_FLAG_DX, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(XBGR2101010, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, false, 4, SDE_FORMAT_FLAG_DX, SDE_FETCH_LINEAR, 1), INTERLEAVED_RGB_FMT(RGBX1010102, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, - C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, false, 4, SDE_FORMAT_FLAG_DX, SDE_FETCH_LINEAR, 1), @@ -378,46 +410,149 @@ static const struct sde_format sde_format_map[] = { }; /* + * A5x tile formats tables: + * These tables hold the A5x tile formats supported. + */ +static const struct sde_format sde_format_map_tile[] = { + INTERLEAVED_RGB_FMT(ARGB8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + true, 4, 0, + SDE_FETCH_UBWC, 1), + + INTERLEAVED_RGB_FMT(ABGR8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, + true, 4, 0, + SDE_FETCH_UBWC, 1), + + INTERLEAVED_RGB_FMT(RGBA8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + true, 4, 0, + SDE_FETCH_UBWC, 1), + + INTERLEAVED_RGB_FMT(BGRA8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + true, 4, 0, + SDE_FETCH_UBWC, 1), + + INTERLEAVED_RGB_FMT(BGRX8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, + false, 4, 0, + SDE_FETCH_UBWC, 1), + + INTERLEAVED_RGB_FMT(XRGB8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, + false, 4, 0, + SDE_FETCH_UBWC, 1), + + INTERLEAVED_RGB_FMT(RGBX8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + false, 4, 0, + SDE_FETCH_UBWC, 1), + + PSEUDO_YUV_FMT(NV12, + 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C1_B_Cb, C2_R_Cr, + SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV, + SDE_FETCH_UBWC, 2), + + PSEUDO_YUV_FMT(NV21, + 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C2_R_Cr, C1_B_Cb, + SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV, + SDE_FETCH_UBWC, 2), +}; + +static const struct sde_format sde_format_map_p010_tile[] = { + PSEUDO_YUV_FMT_LOOSE(NV12, + 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C1_B_Cb, C2_R_Cr, + SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX), + SDE_FETCH_UBWC, 2), +}; + +static const struct sde_format sde_format_map_tp10_tile[] = { + PSEUDO_YUV_FMT(NV12, + 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C1_B_Cb, C2_R_Cr, + SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX), + SDE_FETCH_UBWC, 2), +}; + +/* * UBWC formats table: * This table holds the UBWC formats supported. * If a compression ratio needs to be used for this or any other format, * the data will be passed by user-space. */ static const struct sde_format sde_format_map_ubwc[] = { - INTERLEAVED_RGB_FMT(RGB565, + INTERLEAVED_RGB_FMT(BGR565, 0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3, - false, 2, 0, + false, 2, SDE_FORMAT_FLAG_COMPRESSED, SDE_FETCH_UBWC, 2), - INTERLEAVED_RGB_FMT(RGBA8888, + INTERLEAVED_RGB_FMT(ABGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, - true, 4, 0, + true, 4, SDE_FORMAT_FLAG_COMPRESSED, SDE_FETCH_UBWC, 2), - INTERLEAVED_RGB_FMT(RGBX8888, + INTERLEAVED_RGB_FMT(XBGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, - false, 4, 0, + false, 4, SDE_FORMAT_FLAG_COMPRESSED, SDE_FETCH_UBWC, 2), - INTERLEAVED_RGB_FMT(RGBA1010102, + INTERLEAVED_RGB_FMT(ABGR2101010, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, - true, 4, SDE_FORMAT_FLAG_DX, + true, 4, SDE_FORMAT_FLAG_DX | SDE_FORMAT_FLAG_COMPRESSED, SDE_FETCH_UBWC, 2), - INTERLEAVED_RGB_FMT(RGBX1010102, + INTERLEAVED_RGB_FMT(XBGR2101010, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, - true, 4, SDE_FORMAT_FLAG_DX, + true, 4, SDE_FORMAT_FLAG_DX | SDE_FORMAT_FLAG_COMPRESSED, SDE_FETCH_UBWC, 2), PSEUDO_YUV_FMT(NV12, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C1_B_Cb, C2_R_Cr, - SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV, + SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV | + SDE_FORMAT_FLAG_COMPRESSED, + SDE_FETCH_UBWC, 4), +}; + +static const struct sde_format sde_format_map_p010[] = { + PSEUDO_YUV_FMT_LOOSE(NV12, + 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C1_B_Cb, C2_R_Cr, + SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX), + SDE_FETCH_LINEAR, 2), +}; + +static const struct sde_format sde_format_map_p010_ubwc[] = { + PSEUDO_YUV_FMT_LOOSE(NV12, + 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C1_B_Cb, C2_R_Cr, + SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX | + SDE_FORMAT_FLAG_COMPRESSED), + SDE_FETCH_UBWC, 4), +}; + +static const struct sde_format sde_format_map_tp10_ubwc[] = { + PSEUDO_YUV_FMT(NV12, + 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C1_B_Cb, C2_R_Cr, + SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX | + SDE_FORMAT_FLAG_COMPRESSED), SDE_FETCH_UBWC, 4), }; @@ -452,6 +587,37 @@ static void _sde_get_v_h_subsample_rate( } } +static int _sde_format_get_media_color_ubwc(const struct sde_format *fmt) +{ + static const struct sde_media_color_map sde_media_ubwc_map[] = { + {DRM_FORMAT_ABGR8888, COLOR_FMT_RGBA8888_UBWC}, + {DRM_FORMAT_XBGR8888, COLOR_FMT_RGBA8888_UBWC}, + {DRM_FORMAT_ABGR2101010, COLOR_FMT_RGBA1010102_UBWC}, + {DRM_FORMAT_XBGR2101010, COLOR_FMT_RGBA1010102_UBWC}, + {DRM_FORMAT_BGR565, COLOR_FMT_RGB565_UBWC}, + }; + int color_fmt = -1; + int i; + + if (fmt->base.pixel_format == DRM_FORMAT_NV12) { + if (SDE_FORMAT_IS_DX(fmt)) { + if (fmt->unpack_tight) + color_fmt = COLOR_FMT_NV12_BPP10_UBWC; + else + color_fmt = COLOR_FMT_P010_UBWC; + } else + color_fmt = COLOR_FMT_NV12_UBWC; + return color_fmt; + } + + for (i = 0; i < ARRAY_SIZE(sde_media_ubwc_map); ++i) + if (fmt->base.pixel_format == sde_media_ubwc_map[i].format) { + color_fmt = sde_media_ubwc_map[i].color; + break; + } + return color_fmt; +} + static int _sde_format_get_plane_sizes_ubwc( const struct sde_format *fmt, const uint32_t width, @@ -459,6 +625,8 @@ static int _sde_format_get_plane_sizes_ubwc( struct sde_hw_fmt_layout *layout) { int i; + int color; + bool meta = SDE_FORMAT_IS_UBWC(fmt); memset(layout, 0, sizeof(struct sde_hw_fmt_layout)); layout->format = fmt; @@ -466,86 +634,63 @@ static int _sde_format_get_plane_sizes_ubwc( layout->height = height; layout->num_planes = fmt->num_planes; - if (fmt->base.pixel_format == DRM_FORMAT_NV12) { - uint32_t y_stride_alignment, uv_stride_alignment; - uint32_t y_height_alignment, uv_height_alignment; - uint32_t y_tile_width = 32; - uint32_t y_tile_height = 8; - uint32_t uv_tile_width = y_tile_width / 2; - uint32_t uv_tile_height = y_tile_height; - uint32_t y_bpp_numer = 1, y_bpp_denom = 1; - uint32_t uv_bpp_numer = 1, uv_bpp_denom = 1; - - y_stride_alignment = 128; - uv_stride_alignment = 64; - y_height_alignment = 32; - uv_height_alignment = 32; - y_bpp_numer = 1; - uv_bpp_numer = 2; - y_bpp_denom = 1; - uv_bpp_denom = 1; - - layout->num_planes = 4; - /* Y bitstream stride and plane size */ - layout->plane_pitch[0] = ALIGN(width, y_stride_alignment); - layout->plane_pitch[0] = (layout->plane_pitch[0] * y_bpp_numer) - / y_bpp_denom; - layout->plane_size[0] = ALIGN(layout->plane_pitch[0] * - ALIGN(height, y_height_alignment), 4096); - - /* CbCr bitstream stride and plane size */ - layout->plane_pitch[1] = ALIGN(width / 2, uv_stride_alignment); - layout->plane_pitch[1] = (layout->plane_pitch[1] * uv_bpp_numer) - / uv_bpp_denom; - layout->plane_size[1] = ALIGN(layout->plane_pitch[1] * - ALIGN(height / 2, uv_height_alignment), 4096); - - /* Y meta data stride and plane size */ - layout->plane_pitch[2] = ALIGN( - DIV_ROUND_UP(width, y_tile_width), 64); - layout->plane_size[2] = ALIGN(layout->plane_pitch[2] * - ALIGN(DIV_ROUND_UP(height, y_tile_height), 16), 4096); - - /* CbCr meta data stride and plane size */ - layout->plane_pitch[3] = ALIGN( - DIV_ROUND_UP(width / 2, uv_tile_width), 64); - layout->plane_size[3] = ALIGN(layout->plane_pitch[3] * - ALIGN(DIV_ROUND_UP(height / 2, uv_tile_height), 16), - 4096); - - } else if (fmt->base.pixel_format == DRM_FORMAT_ABGR8888 || - fmt->base.pixel_format == DRM_FORMAT_XBGR8888 || - fmt->base.pixel_format == DRM_FORMAT_BGRA1010102 || - fmt->base.pixel_format == DRM_FORMAT_BGRX1010102 || - fmt->base.pixel_format == DRM_FORMAT_BGR565) { - - uint32_t stride_alignment, aligned_bitstream_width; - - if (fmt->base.pixel_format == DRM_FORMAT_BGR565) - stride_alignment = 128; - else - stride_alignment = 64; - layout->num_planes = 3; - - /* Nothing in plane[1] */ - - /* RGB bitstream stride and plane size */ - aligned_bitstream_width = ALIGN(width, stride_alignment); - layout->plane_pitch[0] = aligned_bitstream_width * fmt->bpp; - layout->plane_size[0] = ALIGN(fmt->bpp * aligned_bitstream_width - * ALIGN(height, 16), 4096); - - /* RGB meta data stride and plane size */ - layout->plane_pitch[2] = ALIGN(DIV_ROUND_UP( - aligned_bitstream_width, 16), 64); - layout->plane_size[2] = ALIGN(layout->plane_pitch[2] * - ALIGN(DIV_ROUND_UP(height, 4), 16), 4096); - } else { + color = _sde_format_get_media_color_ubwc(fmt); + if (color < 0) { DRM_ERROR("UBWC format not supported for fmt:0x%X\n", fmt->base.pixel_format); return -EINVAL; } + if (SDE_FORMAT_IS_YUV(layout->format)) { + uint32_t y_sclines, uv_sclines; + uint32_t y_meta_scanlines = 0; + uint32_t uv_meta_scanlines = 0; + + layout->num_planes = 2; + layout->plane_pitch[0] = VENUS_Y_STRIDE(color, width); + y_sclines = VENUS_Y_SCANLINES(color, height); + layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] * + y_sclines, SDE_UBWC_PLANE_SIZE_ALIGNMENT); + + layout->plane_pitch[1] = VENUS_UV_STRIDE(color, width); + uv_sclines = VENUS_UV_SCANLINES(color, height); + layout->plane_size[1] = MSM_MEDIA_ALIGN(layout->plane_pitch[1] * + uv_sclines, SDE_UBWC_PLANE_SIZE_ALIGNMENT); + + if (!meta) + goto done; + + layout->num_planes += 2; + layout->plane_pitch[2] = VENUS_Y_META_STRIDE(color, width); + y_meta_scanlines = VENUS_Y_META_SCANLINES(color, height); + layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] * + y_meta_scanlines, SDE_UBWC_PLANE_SIZE_ALIGNMENT); + + layout->plane_pitch[3] = VENUS_UV_META_STRIDE(color, width); + uv_meta_scanlines = VENUS_UV_META_SCANLINES(color, height); + layout->plane_size[3] = MSM_MEDIA_ALIGN(layout->plane_pitch[3] * + uv_meta_scanlines, SDE_UBWC_PLANE_SIZE_ALIGNMENT); + + } else { + uint32_t rgb_scanlines, rgb_meta_scanlines; + + layout->num_planes = 1; + + layout->plane_pitch[0] = VENUS_RGB_STRIDE(color, width); + rgb_scanlines = VENUS_RGB_SCANLINES(color, height); + layout->plane_size[0] = MSM_MEDIA_ALIGN(layout->plane_pitch[0] * + rgb_scanlines, SDE_UBWC_PLANE_SIZE_ALIGNMENT); + + if (!meta) + goto done; + layout->num_planes += 2; + layout->plane_pitch[2] = VENUS_RGB_META_STRIDE(color, width); + rgb_meta_scanlines = VENUS_RGB_META_SCANLINES(color, height); + layout->plane_size[2] = MSM_MEDIA_ALIGN(layout->plane_pitch[2] * + rgb_meta_scanlines, SDE_UBWC_PLANE_SIZE_ALIGNMENT); + } + +done: for (i = 0; i < SDE_MAX_PLANES; i++) layout->total_size += layout->plane_size[i]; @@ -574,6 +719,7 @@ static int _sde_format_get_plane_sizes_linear( } else { uint32_t v_subsample, h_subsample; uint32_t chroma_samp; + uint32_t bpp = 1; chroma_samp = fmt->chroma_sample; _sde_get_v_h_subsample_rate(chroma_samp, &v_subsample, @@ -584,8 +730,11 @@ static int _sde_format_get_plane_sizes_linear( return -EINVAL; } - layout->plane_pitch[0] = width; - layout->plane_pitch[1] = width / h_subsample; + if ((fmt->base.pixel_format == DRM_FORMAT_NV12) && + (SDE_FORMAT_IS_DX(fmt))) + bpp = 2; + layout->plane_pitch[0] = width * bpp; + layout->plane_pitch[1] = layout->plane_pitch[0] / h_subsample; layout->plane_size[0] = layout->plane_pitch[0] * height; layout->plane_size[1] = layout->plane_pitch[1] * (height / v_subsample); @@ -608,7 +757,7 @@ static int _sde_format_get_plane_sizes_linear( return 0; } -static int _sde_format_get_plane_sizes( +int sde_format_get_plane_sizes( const struct sde_format *fmt, const uint32_t w, const uint32_t h, @@ -624,7 +773,7 @@ static int _sde_format_get_plane_sizes( return -ERANGE; } - if (SDE_FORMAT_IS_UBWC(fmt)) + if (SDE_FORMAT_IS_UBWC(fmt) || SDE_FORMAT_IS_TILE(fmt)) return _sde_format_get_plane_sizes_ubwc(fmt, w, h, layout); return _sde_format_get_plane_sizes_linear(fmt, w, h, layout); @@ -636,6 +785,7 @@ static int _sde_format_populate_addrs_ubwc( struct sde_hw_fmt_layout *layout) { uint32_t base_addr; + bool meta; if (!fb || !layout) { DRM_ERROR("invalid pointers\n"); @@ -648,6 +798,8 @@ static int _sde_format_populate_addrs_ubwc( return -EFAULT; } + meta = SDE_FORMAT_IS_UBWC(layout->format); + /* Per-format logic for verifying active planes */ if (SDE_FORMAT_IS_YUV(layout->format)) { /************************************************/ @@ -677,6 +829,9 @@ static int _sde_format_populate_addrs_ubwc( layout->plane_addr[1] = base_addr + layout->plane_size[0] + layout->plane_size[2] + layout->plane_size[3]; + if (!meta) + goto done; + /* configure Y metadata plane */ layout->plane_addr[2] = base_addr; @@ -704,10 +859,14 @@ static int _sde_format_populate_addrs_ubwc( layout->plane_addr[0] = base_addr + layout->plane_size[2]; layout->plane_addr[1] = 0; + + if (!meta) + goto done; + layout->plane_addr[2] = base_addr; layout->plane_addr[3] = 0; } - +done: return 0; } @@ -761,7 +920,7 @@ int sde_format_populate_layout( layout->format = to_sde_format(msm_framebuffer_format(fb)); /* Populate the plane sizes etc via get_format */ - ret = _sde_format_get_plane_sizes(layout->format, fb->width, fb->height, + ret = sde_format_get_plane_sizes(layout->format, fb->width, fb->height, layout); if (ret) return ret; @@ -770,7 +929,8 @@ int sde_format_populate_layout( plane_addr[i] = layout->plane_addr[i]; /* Populate the addresses given the fb */ - if (SDE_FORMAT_IS_UBWC(layout->format)) + if (SDE_FORMAT_IS_UBWC(layout->format) || + SDE_FORMAT_IS_TILE(layout->format)) ret = _sde_format_populate_addrs_ubwc(aspace, fb, layout); else ret = _sde_format_populate_addrs_linear(aspace, fb, layout); @@ -864,7 +1024,7 @@ int sde_format_check_modified_format( fmt = to_sde_format(msm_fmt); num_base_fmt_planes = drm_format_num_planes(fmt->base.pixel_format); - ret = _sde_format_get_plane_sizes(fmt, cmd->width, cmd->height, + ret = sde_format_get_plane_sizes(fmt, cmd->width, cmd->height, &layout); if (ret) return ret; @@ -874,7 +1034,8 @@ int sde_format_check_modified_format( DRM_ERROR("invalid handle for plane %d\n", i); return -EINVAL; } - bos_total_size += bos[i]->size; + if ((i == 0) || (bos[i] != bos[0])) + bos_total_size += bos[i]->size; } if (bos_total_size < layout.total_size) { @@ -902,14 +1063,14 @@ const struct sde_format *sde_get_sde_format_ext( * All planes used must specify the same modifier. */ if (modifiers_len && !modifiers) { - DRM_ERROR("invalid modifiers array\n"); + SDE_ERROR("invalid modifiers array\n"); return NULL; } else if (modifiers && modifiers_len && modifiers[0]) { mod0 = modifiers[0]; - DBG("plane format modifier 0x%llX", mod0); + SDE_DEBUG("plane format modifier 0x%llX\n", mod0); for (i = 1; i < modifiers_len; i++) { if (modifiers[i] != mod0) { - DRM_ERROR("bad fmt mod 0x%llX on plane %d\n", + SDE_ERROR("bad fmt mod 0x%llX on plane %d\n", modifiers[i], i); return NULL; } @@ -922,12 +1083,55 @@ const struct sde_format *sde_get_sde_format_ext( map_size = ARRAY_SIZE(sde_format_map); break; case DRM_FORMAT_MOD_QCOM_COMPRESSED: + case DRM_FORMAT_MOD_QCOM_COMPRESSED | DRM_FORMAT_MOD_QCOM_TILE: map = sde_format_map_ubwc; map_size = ARRAY_SIZE(sde_format_map_ubwc); - DBG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED", format); + SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED\n", + format); + break; + case DRM_FORMAT_MOD_QCOM_DX: + map = sde_format_map_p010; + map_size = ARRAY_SIZE(sde_format_map_p010); + SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_DX\n", format); + break; + case (DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_COMPRESSED): + case (DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_COMPRESSED | + DRM_FORMAT_MOD_QCOM_TILE): + map = sde_format_map_p010_ubwc; + map_size = ARRAY_SIZE(sde_format_map_p010_ubwc); + SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED/DX\n", + format); + break; + case (DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_COMPRESSED | + DRM_FORMAT_MOD_QCOM_TIGHT): + case (DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_COMPRESSED | + DRM_FORMAT_MOD_QCOM_TIGHT | DRM_FORMAT_MOD_QCOM_TILE): + map = sde_format_map_tp10_ubwc; + map_size = ARRAY_SIZE(sde_format_map_tp10_ubwc); + SDE_DEBUG( + "found fmt 0x%X DRM_FORMAT_MOD_QCOM_COMPRESSED/DX/TIGHT\n", + format); + break; + case DRM_FORMAT_MOD_QCOM_TILE: + map = sde_format_map_tile; + map_size = ARRAY_SIZE(sde_format_map_tile); + SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_TILE\n", format); + break; + case (DRM_FORMAT_MOD_QCOM_TILE | DRM_FORMAT_MOD_QCOM_DX): + map = sde_format_map_p010_tile; + map_size = ARRAY_SIZE(sde_format_map_p010_tile); + SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_TILE/DX\n", + format); + break; + case (DRM_FORMAT_MOD_QCOM_TILE | DRM_FORMAT_MOD_QCOM_DX | + DRM_FORMAT_MOD_QCOM_TIGHT): + map = sde_format_map_tp10_tile; + map_size = ARRAY_SIZE(sde_format_map_tp10_tile); + SDE_DEBUG("found fmt 0x%X DRM_FORMAT_MOD_QCOM_TILE/DX/TIGHT\n", + format); break; default: - DRM_ERROR("unsupported format modifier %llX\n", mod0); + SDE_ERROR("unsupported format modifier %llX\n", mod0); return NULL; } @@ -939,10 +1143,10 @@ const struct sde_format *sde_get_sde_format_ext( } if (fmt == NULL) - DRM_ERROR("unsupported fmt 0x%X modifier 0x%llX\n", + SDE_ERROR("unsupported fmt 0x%X modifier 0x%llX\n", format, mod0); else - DBG("fmt %s mod 0x%llX ubwc %d yuv %d", + SDE_DEBUG("fmt %s mod 0x%llX ubwc %d yuv %d\n", drm_get_format_name(format), mod0, SDE_FORMAT_IS_UBWC(fmt), SDE_FORMAT_IS_YUV(fmt)); diff --git a/drivers/gpu/drm/msm/sde/sde_formats.h b/drivers/gpu/drm/msm/sde/sde_formats.h index 0de081d619b7..ec8f97da4a41 100644 --- a/drivers/gpu/drm/msm/sde/sde_formats.h +++ b/drivers/gpu/drm/msm/sde/sde_formats.h @@ -59,6 +59,21 @@ uint32_t sde_populate_formats( uint32_t pixel_formats_max); /** + * sde_format_get_plane_sizes - calculate size and layout of given buffer format + * @fmt: pointer to sde_format + * @w: width of the buffer + * @h: height of the buffer + * @layout: layout of the buffer + * + * Return: size of the buffer + */ +int sde_format_get_plane_sizes( + const struct sde_format *fmt, + const uint32_t w, + const uint32_t h, + struct sde_hw_fmt_layout *layout); + +/** * sde_format_check_modified_format - validate format and buffers for * sde non-standard, i.e. modified format * @kms: kms driver diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c index 519288f0dda2..eb398fbee816 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c @@ -14,6 +14,8 @@ #include <linux/slab.h> #include <linux/of_address.h> +#include <linux/of_platform.h> + #include "sde_hw_mdss.h" #include "sde_hw_catalog.h" #include "sde_hw_catalog_format.h" @@ -403,6 +405,38 @@ static struct sde_prop_type vbif_prop[] = { /************************************************************* * static API list *************************************************************/ + +/** + * _sde_copy_formats - copy formats from src_list to dst_list + * @dst_list: pointer to destination list where to copy formats + * @dst_list_size: size of destination list + * @dst_list_pos: starting position on the list where to copy formats + * @src_list: pointer to source list where to copy formats from + * @src_list_size: size of source list + * Return: number of elements populated + */ +static uint32_t _sde_copy_formats( + struct sde_format_extended *dst_list, + uint32_t dst_list_size, + uint32_t dst_list_pos, + const struct sde_format_extended *src_list, + uint32_t src_list_size) +{ + uint32_t cur_pos, i; + + if (!dst_list || !src_list || (dst_list_pos >= (dst_list_size - 1))) + return 0; + + for (i = 0, cur_pos = dst_list_pos; + (cur_pos < (dst_list_size - 1)) && (i < src_list_size) + && src_list[i].fourcc_format; ++i, ++cur_pos) + dst_list[cur_pos] = src_list[i]; + + dst_list[cur_pos].fourcc_format = 0; + + return i; +} + static int _parse_dt_u32_handler(struct device_node *np, char *prop_name, u32 *offsets, int len, bool mandatory) { @@ -469,6 +503,7 @@ static int _validate_dt_entry(struct device_node *np, rc = -EINVAL; } *off_count = 0; + memset(prop_count, 0, sizeof(int) * prop_size); return rc; } } @@ -656,7 +691,7 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, sblk->maxdwnscale = MAX_SSPP_DOWNSCALE; sspp->id = SSPP_VIG0 + *vig_count; sspp->clk_ctrl = SDE_CLK_CTRL_VIG0 + *vig_count; - sblk->format_list = plane_formats_yuv; + sspp->type = SSPP_TYPE_VIG; set_bit(SDE_SSPP_QOS, &sspp->features); (*vig_count)++; @@ -715,6 +750,7 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, sblk->pcc_blk.len = 0; set_bit(SDE_SSPP_PCC, &sspp->features); } + snprintf(sspp->name, sizeof(sspp->name), "vig%d", *vig_count-1); } static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg, @@ -725,7 +761,7 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg, sblk->maxdwnscale = MAX_SSPP_DOWNSCALE; sspp->id = SSPP_RGB0 + *rgb_count; sspp->clk_ctrl = SDE_CLK_CTRL_RGB0 + *rgb_count; - sblk->format_list = plane_formats; + sspp->type = SSPP_TYPE_RGB; set_bit(SDE_SSPP_QOS, &sspp->features); (*rgb_count)++; @@ -753,6 +789,7 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg, sblk->pcc_blk.len = 0; set_bit(SDE_SSPP_PCC, &sspp->features); } + snprintf(sspp->name, sizeof(sspp->name), "rgb%d", *rgb_count-1); } static void _sde_sspp_setup_cursor(struct sde_mdss_cfg *sde_cfg, @@ -764,8 +801,9 @@ static void _sde_sspp_setup_cursor(struct sde_mdss_cfg *sde_cfg, sblk->maxdwnscale = SSPP_UNITY_SCALE; sspp->id = SSPP_CURSOR0 + *cursor_count; sspp->clk_ctrl = SDE_CLK_CTRL_CURSOR0 + *cursor_count; - sblk->format_list = plane_formats; + sspp->type = SSPP_TYPE_CURSOR; (*cursor_count)++; + snprintf(sspp->name, sizeof(sspp->name), "cursor%d", *cursor_count-1); } static void _sde_sspp_setup_dma(struct sde_mdss_cfg *sde_cfg, @@ -776,9 +814,10 @@ static void _sde_sspp_setup_dma(struct sde_mdss_cfg *sde_cfg, sblk->maxdwnscale = SSPP_UNITY_SCALE; sspp->id = SSPP_DMA0 + *dma_count; sspp->clk_ctrl = SDE_CLK_CTRL_DMA0 + *dma_count; - sblk->format_list = plane_formats; + sspp->type = SSPP_TYPE_DMA; set_bit(SDE_SSPP_QOS, &sspp->features); (*dma_count)++; + snprintf(sspp->name, sizeof(sspp->name), "dma%d", *dma_count-1); } static int sde_sspp_parse_dt(struct device_node *np, @@ -1200,7 +1239,8 @@ end: return rc; } -static int sde_wb_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) +static int sde_wb_parse_dt(struct device_node *np, + struct sde_mdss_cfg *sde_cfg) { int rc, prop_count[WB_PROP_MAX], i, j; struct sde_prop_value *prop_value = NULL; @@ -1251,7 +1291,6 @@ static int sde_wb_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) wb->xin_id = PROP_VALUE_ACCESS(prop_value, WB_XIN_ID, i); wb->vbif_idx = VBIF_NRT; wb->len = PROP_VALUE_ACCESS(prop_value, WB_LEN, 0); - wb->format_list = wb2_formats; if (!prop_exists[WB_LEN]) wb->len = DEFAULT_SDE_HW_BLOCK_LEN; sblk->maxlinewidth = sde_cfg->max_wb_linewidth; @@ -1686,7 +1725,8 @@ end: return rc; } -static int sde_pp_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) +static int sde_pp_parse_dt(struct device_node *np, + struct sde_mdss_cfg *sde_cfg) { int rc, prop_count[PP_PROP_MAX], i; struct sde_prop_value *prop_value = NULL; @@ -1760,6 +1800,94 @@ end: return rc; } +static inline u32 _sde_parse_sspp_id(struct sde_mdss_cfg *cfg, + const char *name) +{ + int i; + + for (i = 0; i < cfg->sspp_count; i++) { + if (!strcmp(cfg->sspp[i].name, name)) + return cfg->sspp[i].id; + } + + return SSPP_NONE; +} + +static int _sde_vp_parse_dt(struct device_node *np, + struct sde_mdss_cfg *cfg) +{ + int rc = 0, i = 0; + struct device_node *node = NULL; + struct device_node *root_node = NULL; + struct sde_vp_cfg *vp; + struct sde_vp_sub_blks *vp_sub, *vp_sub_next; + struct property *prop; + const char *cname; + + root_node = of_get_child_by_name(np, "qcom,sde-plane-id-map"); + if (!root_node) { + root_node = of_parse_phandle(np, "qcom,sde-plane-id-map", 0); + if (!root_node) { + SDE_ERROR("No entry present for qcom,sde-plane-id-map"); + rc = -EINVAL; + goto end; + } + } + + for_each_child_of_node(root_node, node) { + if (i >= MAX_BLOCKS) { + SDE_ERROR("num of nodes(%d) is bigger than max(%d)\n", + i, MAX_BLOCKS); + rc = -EINVAL; + goto end; + } + cfg->vp_count++; + vp = &(cfg->vp[i]); + vp->id = i; + rc = of_property_read_string(node, "qcom,display-type", + &(vp->display_type)); + if (rc) { + SDE_ERROR("failed to read display-type, rc = %d\n", rc); + goto end; + } + + rc = of_property_read_string(node, "qcom,plane-type", + &(vp->plane_type)); + if (rc) { + SDE_ERROR("failed to read plane-type, rc = %d\n", rc); + goto end; + } + + INIT_LIST_HEAD(&vp->sub_blks); + of_property_for_each_string(node, "qcom,plane-name", + prop, cname) { + vp_sub = kzalloc(sizeof(*vp_sub), GFP_KERNEL); + if (!vp_sub) { + rc = -ENOMEM; + goto end; + } + vp_sub->sspp_id = _sde_parse_sspp_id(cfg, cname); + list_add_tail(&vp_sub->pipeid_list, &vp->sub_blks); + } + i++; + } + +end: + if (rc && cfg->vp_count) { + vp = &(cfg->vp[i]); + for (i = 0; i < cfg->vp_count; i++) { + list_for_each_entry_safe(vp_sub, vp_sub_next, + &vp->sub_blks, pipeid_list) { + list_del(&vp_sub->pipeid_list); + kfree(vp_sub); + } + } + memset(&(cfg->vp[0]), 0, sizeof(cfg->vp)); + cfg->vp_count = 0; + } + return rc; +} + static int sde_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) { int rc, len, prop_count[SDE_PROP_MAX]; @@ -1851,7 +1979,8 @@ end: return rc; } -static int sde_perf_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) +static int sde_perf_parse_dt(struct device_node *np, + struct sde_mdss_cfg *cfg) { int rc, len, prop_count[PERF_PROP_MAX]; struct sde_prop_value *prop_value = NULL; @@ -1891,8 +2020,124 @@ end: return rc; } -static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) +static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg, + uint32_t hw_rev) { + int i, rc = 0; + uint32_t dma_list_size, vig_list_size, wb2_list_size; + uint32_t cursor_list_size = 0; + struct sde_sspp_sub_blks *sblk; + uint32_t index = 0; + + if (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_300)) { + cursor_list_size = ARRAY_SIZE(cursor_formats); + sde_cfg->cursor_formats = kcalloc(cursor_list_size, + sizeof(struct sde_format_extended), GFP_KERNEL); + if (!sde_cfg->cursor_formats) { + rc = -ENOMEM; + goto end; + } + index = _sde_copy_formats(sde_cfg->cursor_formats, + cursor_list_size, 0, cursor_formats, + ARRAY_SIZE(cursor_formats)); + } + + dma_list_size = ARRAY_SIZE(plane_formats); + vig_list_size = ARRAY_SIZE(plane_formats_yuv); + wb2_list_size = ARRAY_SIZE(wb2_formats); + + dma_list_size += ARRAY_SIZE(rgb_10bit_formats); + vig_list_size += ARRAY_SIZE(rgb_10bit_formats) + + ARRAY_SIZE(tp10_ubwc_formats) + + ARRAY_SIZE(p010_formats); + wb2_list_size += ARRAY_SIZE(rgb_10bit_formats) + + ARRAY_SIZE(tp10_ubwc_formats); + + sde_cfg->dma_formats = kcalloc(dma_list_size, + sizeof(struct sde_format_extended), GFP_KERNEL); + if (!sde_cfg->dma_formats) { + rc = -ENOMEM; + goto end; + } + + sde_cfg->vig_formats = kcalloc(vig_list_size, + sizeof(struct sde_format_extended), GFP_KERNEL); + if (!sde_cfg->vig_formats) { + rc = -ENOMEM; + goto end; + } + + sde_cfg->wb_formats = kcalloc(wb2_list_size, + sizeof(struct sde_format_extended), GFP_KERNEL); + if (!sde_cfg->wb_formats) { + SDE_ERROR("failed to allocate wb format list\n"); + rc = -ENOMEM; + goto end; + } + + index = _sde_copy_formats(sde_cfg->dma_formats, dma_list_size, + 0, plane_formats, ARRAY_SIZE(plane_formats)); + index += _sde_copy_formats(sde_cfg->dma_formats, dma_list_size, + index, rgb_10bit_formats, + ARRAY_SIZE(rgb_10bit_formats)); + + index = _sde_copy_formats(sde_cfg->vig_formats, vig_list_size, + 0, plane_formats_yuv, ARRAY_SIZE(plane_formats_yuv)); + index += _sde_copy_formats(sde_cfg->vig_formats, vig_list_size, + index, rgb_10bit_formats, + ARRAY_SIZE(rgb_10bit_formats)); + index += _sde_copy_formats(sde_cfg->vig_formats, vig_list_size, + index, p010_formats, ARRAY_SIZE(p010_formats)); + + index += _sde_copy_formats(sde_cfg->vig_formats, vig_list_size, + index, tp10_ubwc_formats, + ARRAY_SIZE(tp10_ubwc_formats)); + + index = _sde_copy_formats(sde_cfg->wb_formats, wb2_list_size, + 0, wb2_formats, ARRAY_SIZE(wb2_formats)); + index += _sde_copy_formats(sde_cfg->wb_formats, wb2_list_size, + index, rgb_10bit_formats, + ARRAY_SIZE(rgb_10bit_formats)); + index += _sde_copy_formats(sde_cfg->wb_formats, wb2_list_size, + index, tp10_ubwc_formats, + ARRAY_SIZE(tp10_ubwc_formats)); + + for (i = 0; i < sde_cfg->sspp_count; ++i) { + struct sde_sspp_cfg *sspp = &sde_cfg->sspp[i]; + + sblk = (struct sde_sspp_sub_blks *)sspp->sblk; + switch (sspp->type) { + case SSPP_TYPE_VIG: + sblk->format_list = sde_cfg->vig_formats; + break; + case SSPP_TYPE_CURSOR: + if (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_300)) + sblk->format_list = sde_cfg->cursor_formats; + else + SDE_ERROR("invalid sspp type %d, xin id %d\n", + sspp->type, sspp->xin_id); + break; + case SSPP_TYPE_DMA: + sblk->format_list = sde_cfg->dma_formats; + break; + default: + SDE_ERROR("invalid sspp type %d\n", sspp->type); + rc = -EINVAL; + goto end; + } + } + + for (i = 0; i < sde_cfg->wb_count; ++i) + sde_cfg->wb[i].format_list = sde_cfg->wb_formats; + +end: + return rc; +} + +static int sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) +{ + int rc = 0; + switch (hw_rev) { case SDE_HW_VER_170: case SDE_HW_VER_171: @@ -1900,15 +2145,20 @@ static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) /* update msm8996 target here */ break; case SDE_HW_VER_300: + case SDE_HW_VER_301: case SDE_HW_VER_400: /* update cobalt and skunk target here */ + rc = sde_hardware_format_caps(sde_cfg, hw_rev); break; } + + return rc; } void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg) { int i; + struct sde_vp_sub_blks *vp_sub, *vp_sub_next; if (!sde_cfg) return; @@ -1932,13 +2182,28 @@ void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg) kfree(sde_cfg->vbif[i].dynamic_ot_rd_tbl.cfg); kfree(sde_cfg->vbif[i].dynamic_ot_wr_tbl.cfg); } + + for (i = 0; i < sde_cfg->vp_count; i++) { + list_for_each_entry_safe(vp_sub, vp_sub_next, + &sde_cfg->vp[i].sub_blks, pipeid_list) { + list_del(&vp_sub->pipeid_list); + kfree(vp_sub); + } + } + + kfree(sde_cfg->dma_formats); + kfree(sde_cfg->cursor_formats); + kfree(sde_cfg->vig_formats); + kfree(sde_cfg->wb_formats); + kfree(sde_cfg); } /************************************************************* * hardware catalog init *************************************************************/ -struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev) +struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, + u32 hw_rev) { int rc; struct sde_mdss_cfg *sde_cfg; @@ -1996,7 +2261,13 @@ struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev) if (rc) goto end; - sde_hardware_caps(sde_cfg, hw_rev); + rc = _sde_vp_parse_dt(np, sde_cfg); + if (rc) + SDE_DEBUG("virtual plane is not supported.\n"); + + rc = sde_hardware_caps(sde_cfg, hw_rev); + if (rc) + goto end; return sde_cfg; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h index a8f9169aaf35..01204df48871 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h @@ -42,7 +42,8 @@ #define SDE_HW_VER_170 SDE_HW_VER(1, 7, 0) /* 8996 v1.0 */ #define SDE_HW_VER_171 SDE_HW_VER(1, 7, 1) /* 8996 v2.0 */ #define SDE_HW_VER_172 SDE_HW_VER(1, 7, 2) /* 8996 v3.0 */ -#define SDE_HW_VER_300 SDE_HW_VER(3, 0, 0) /* cobalt v1.0 */ +#define SDE_HW_VER_300 SDE_HW_VER(3, 0, 0) /* 8998 v1.0 */ +#define SDE_HW_VER_301 SDE_HW_VER(3, 0, 1) /* 8998 v1.1 */ #define SDE_HW_VER_400 SDE_HW_VER(4, 0, 0) /* msmskunk v1.0 */ #define IS_MSMSKUNK_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_400) @@ -57,6 +58,8 @@ #define SDE_COLOR_PROCESS_MAJOR(version) (((version) & 0xFFFF0000) >> 16) #define SDE_COLOR_PROCESS_MINOR(version) ((version) & 0xFFFF) +#define SSPP_NAME_SIZE 12 + /** * MDP TOP BLOCK features * @SDE_MDP_PANIC_PER_PIPE Panic configuration needs to be be done per pipe @@ -455,12 +458,16 @@ struct sde_ctl_cfg { * @sblk: SSPP sub-blocks information * @xin_id: bus client identifier * @clk_ctrl clock control identifier + * @name source pipe name + * @type sspp type identifier */ struct sde_sspp_cfg { SDE_HW_BLK_INFO; const struct sde_sspp_sub_blks *sblk; u32 xin_id; enum sde_clk_ctrl_type clk_ctrl; + char name[SSPP_NAME_SIZE]; + u32 type; }; /** @@ -608,6 +615,31 @@ struct sde_perf_cfg { }; /** +* struct sde_vp_sub_blks - Virtual Plane sub-blocks +* @pipeid_list list for hw pipe id +* @sspp_id SSPP ID, refer to enum sde_sspp. +*/ +struct sde_vp_sub_blks { + struct list_head pipeid_list; + u32 sspp_id; +}; + +/** +* struct sde_vp_cfg - information of Virtual Plane SW blocks +* @id enum identifying this block +* @sub_blks list head for virtual plane sub blocks +* @plane_type plane type, such as primary, overlay or cursor +* @display_type which display the plane bound to, such as primary, +* secondary or tertiary +*/ +struct sde_vp_cfg { + u32 id; + struct list_head sub_blks; + const char *plane_type; + const char *display_type; +}; + +/** * struct sde_mdss_cfg - information of MDSS HW * This is the main catalog data structure representing * this HW version. Contains number of instances, @@ -623,6 +655,10 @@ struct sde_perf_cfg { * @csc_type csc or csc_10bit support. * @has_src_split source split feature status * @has_cdp Client driver prefetch feature status + * @dma_formats Supported formats for dma pipe + * @cursor_formats Supported formats for cursor pipe + * @vig_formats Supported formats for vig pipe + * @wb_formats Supported formats for wb */ struct sde_mdss_cfg { u32 hwversion; @@ -672,6 +708,14 @@ struct sde_mdss_cfg { /* Add additional block data structures here */ struct sde_perf_cfg perf; + + u32 vp_count; + struct sde_vp_cfg vp[MAX_BLOCKS]; + + struct sde_format_extended *dma_formats; + struct sde_format_extended *cursor_formats; + struct sde_format_extended *vig_formats; + struct sde_format_extended *wb_formats; }; struct sde_mdss_hw_cfg_handler { diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h index 296694422653..dbc8981a7f8f 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog_format.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -16,17 +16,17 @@ static const struct sde_format_extended plane_formats[] = { {DRM_FORMAT_ARGB8888, 0}, {DRM_FORMAT_ABGR8888, 0}, {DRM_FORMAT_RGBA8888, 0}, - {DRM_FORMAT_RGBA8888, DRM_FORMAT_MOD_QCOM_COMPRESSED}, + {DRM_FORMAT_ABGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED}, {DRM_FORMAT_BGRA8888, 0}, {DRM_FORMAT_XRGB8888, 0}, {DRM_FORMAT_RGBX8888, 0}, {DRM_FORMAT_BGRX8888, 0}, {DRM_FORMAT_XBGR8888, 0}, - {DRM_FORMAT_RGBX8888, DRM_FORMAT_MOD_QCOM_COMPRESSED}, + {DRM_FORMAT_XBGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED}, {DRM_FORMAT_RGB888, 0}, {DRM_FORMAT_BGR888, 0}, {DRM_FORMAT_RGB565, 0}, - {DRM_FORMAT_RGB565, DRM_FORMAT_MOD_QCOM_COMPRESSED}, + {DRM_FORMAT_BGR565, DRM_FORMAT_MOD_QCOM_COMPRESSED}, {DRM_FORMAT_BGR565, 0}, {DRM_FORMAT_ARGB1555, 0}, {DRM_FORMAT_ABGR1555, 0}, @@ -52,16 +52,16 @@ static const struct sde_format_extended plane_formats_yuv[] = { {DRM_FORMAT_ABGR8888, 0}, {DRM_FORMAT_RGBA8888, 0}, {DRM_FORMAT_BGRX8888, 0}, - {DRM_FORMAT_RGBA8888, DRM_FORMAT_MOD_QCOM_COMPRESSED}, + {DRM_FORMAT_ABGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED}, {DRM_FORMAT_BGRA8888, 0}, {DRM_FORMAT_XRGB8888, 0}, {DRM_FORMAT_XBGR8888, 0}, {DRM_FORMAT_RGBX8888, 0}, - {DRM_FORMAT_RGBX8888, DRM_FORMAT_MOD_QCOM_COMPRESSED}, + {DRM_FORMAT_XBGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED}, {DRM_FORMAT_RGB888, 0}, {DRM_FORMAT_BGR888, 0}, {DRM_FORMAT_RGB565, 0}, - {DRM_FORMAT_RGB565, DRM_FORMAT_MOD_QCOM_COMPRESSED}, + {DRM_FORMAT_BGR565, DRM_FORMAT_MOD_QCOM_COMPRESSED}, {DRM_FORMAT_BGR565, 0}, {DRM_FORMAT_ARGB1555, 0}, {DRM_FORMAT_ABGR1555, 0}, @@ -94,13 +94,33 @@ static const struct sde_format_extended plane_formats_yuv[] = { {0, 0}, }; +static const struct sde_format_extended cursor_formats[] = { + {DRM_FORMAT_ARGB8888, 0}, + {DRM_FORMAT_ABGR8888, 0}, + {DRM_FORMAT_RGBA8888, 0}, + {DRM_FORMAT_BGRA8888, 0}, + {DRM_FORMAT_XRGB8888, 0}, + {DRM_FORMAT_ARGB1555, 0}, + {DRM_FORMAT_ABGR1555, 0}, + {DRM_FORMAT_RGBA5551, 0}, + {DRM_FORMAT_BGRA5551, 0}, + {DRM_FORMAT_ARGB4444, 0}, + {DRM_FORMAT_ABGR4444, 0}, + {DRM_FORMAT_RGBA4444, 0}, + {DRM_FORMAT_BGRA4444, 0}, + {0, 0}, +}; + static const struct sde_format_extended wb2_formats[] = { {DRM_FORMAT_RGB565, 0}, + {DRM_FORMAT_BGR565, DRM_FORMAT_MOD_QCOM_COMPRESSED}, {DRM_FORMAT_RGB888, 0}, {DRM_FORMAT_ARGB8888, 0}, {DRM_FORMAT_RGBA8888, 0}, + {DRM_FORMAT_ABGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED}, {DRM_FORMAT_XRGB8888, 0}, {DRM_FORMAT_RGBX8888, 0}, + {DRM_FORMAT_XBGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED}, {DRM_FORMAT_ARGB1555, 0}, {DRM_FORMAT_RGBA5551, 0}, {DRM_FORMAT_XRGB1555, 0}, @@ -127,8 +147,31 @@ static const struct sde_format_extended wb2_formats[] = { {DRM_FORMAT_YUV420, 0}, {DRM_FORMAT_NV12, 0}, + {DRM_FORMAT_NV12, DRM_FORMAT_MOD_QCOM_COMPRESSED}, {DRM_FORMAT_NV16, 0}, {DRM_FORMAT_YUYV, 0}, {0, 0}, }; + +static const struct sde_format_extended rgb_10bit_formats[] = { + {DRM_FORMAT_BGRA1010102, 0}, + {DRM_FORMAT_BGRX1010102, 0}, + {DRM_FORMAT_RGBA1010102, 0}, + {DRM_FORMAT_RGBX1010102, 0}, + {DRM_FORMAT_ABGR2101010, 0}, + {DRM_FORMAT_ABGR2101010, DRM_FORMAT_MOD_QCOM_COMPRESSED}, + {DRM_FORMAT_XBGR2101010, 0}, + {DRM_FORMAT_XBGR2101010, DRM_FORMAT_MOD_QCOM_COMPRESSED}, + {DRM_FORMAT_ARGB2101010, 0}, + {DRM_FORMAT_XRGB2101010, 0}, +}; + +static const struct sde_format_extended p010_formats[] = { + {DRM_FORMAT_NV12, DRM_FORMAT_MOD_QCOM_DX}, +}; + +static const struct sde_format_extended tp10_ubwc_formats[] = { + {DRM_FORMAT_NV12, DRM_FORMAT_MOD_QCOM_COMPRESSED | + DRM_FORMAT_MOD_QCOM_DX | DRM_FORMAT_MOD_QCOM_TIGHT}, +}; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c index c7cbb93bece4..188649d946d6 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -88,50 +88,12 @@ static struct sde_cdm_cfg *_cdm_offset(enum sde_cdm cdm, return ERR_PTR(-EINVAL); } -static void sde_hw_cdm_setup_csc_10bit(struct sde_hw_cdm *ctx, +static int sde_hw_cdm_setup_csc_10bit(struct sde_hw_cdm *ctx, struct sde_csc_cfg *data) { - struct sde_hw_blk_reg_map *c = &ctx->hw; - u32 csc_reg_off = CDM_CSC_10_MATRIX_COEFF_0; - u32 val; - - /* matrix coeff */ - val = data->csc_mv[0] | (data->csc_mv[1] << 16); - SDE_REG_WRITE(c, csc_reg_off, val); - val = data->csc_mv[2] | (data->csc_mv[3] << 16); - SDE_REG_WRITE(c, csc_reg_off + 0x4, val); - val = data->csc_mv[4] | (data->csc_mv[5] << 16); - SDE_REG_WRITE(c, csc_reg_off + 0x8, val); - val = data->csc_mv[6] | (data->csc_mv[7] << 16); - SDE_REG_WRITE(c, csc_reg_off + 0xc, val); - val = data->csc_mv[8]; - SDE_REG_WRITE(c, csc_reg_off + 0x10, val); - - /* Pre clamp */ - val = (data->csc_pre_lv[0] << 16) | data->csc_pre_lv[1]; - SDE_REG_WRITE(c, csc_reg_off + 0x14, val); - val = (data->csc_pre_lv[2] << 16) | data->csc_pre_lv[3]; - SDE_REG_WRITE(c, csc_reg_off + 0x18, val); - val = (data->csc_pre_lv[4] << 16) | data->csc_pre_lv[5]; - SDE_REG_WRITE(c, csc_reg_off + 0x1c, val); - - /* Post clamp */ - val = (data->csc_post_lv[0] << 16) | data->csc_post_lv[1]; - SDE_REG_WRITE(c, csc_reg_off + 0x20, val); - val = (data->csc_post_lv[2] << 16) | data->csc_post_lv[3]; - SDE_REG_WRITE(c, csc_reg_off + 0x24, val); - val = (data->csc_post_lv[4] << 16) | data->csc_post_lv[5]; - SDE_REG_WRITE(c, csc_reg_off + 0x28, val); - - /* Pre-Bias */ - SDE_REG_WRITE(c, csc_reg_off + 0x2c, data->csc_pre_bv[0]); - SDE_REG_WRITE(c, csc_reg_off + 0x30, data->csc_pre_bv[1]); - SDE_REG_WRITE(c, csc_reg_off + 0x34, data->csc_pre_bv[2]); - - /* Post-Bias */ - SDE_REG_WRITE(c, csc_reg_off + 0x38, data->csc_post_bv[0]); - SDE_REG_WRITE(c, csc_reg_off + 0x3c, data->csc_post_bv[1]); - SDE_REG_WRITE(c, csc_reg_off + 0x40, data->csc_post_bv[2]); + sde_hw_csc_setup(&ctx->hw, CDM_CSC_10_MATRIX_COEFF_0, data, true); + + return 0; } static int sde_hw_cdm_setup_cdwn(struct sde_hw_cdm *ctx, diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.h b/drivers/gpu/drm/msm/sde/sde_hw_cdm.h index 264b8a418573..a0afd897e867 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -65,8 +65,9 @@ struct sde_hw_cdm_ops { * to program a different matrix than default matrix. * @cdm: Pointer to the chroma down context structure * @data Pointer to CSC configuration data + * return: 0 if success; error code otherwise */ - void (*setup_csc_data)(struct sde_hw_cdm *cdm, + int (*setup_csc_data)(struct sde_hw_cdm *cdm, struct sde_csc_cfg *data); /** diff --git a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c index 49930365d989..1535d1d1ade5 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -357,37 +357,32 @@ static const struct sde_irq_type sde_irq_map[] = { SDE_INTR_HIST_VIG_1_RSTSEQ_DONE, 2}, { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, - /* irq_idx: 68-71 */ + /* irq_idx: 72-75 */ { SDE_IRQ_TYPE_HIST_VIG_DONE, SSPP_VIG2, SDE_INTR_HIST_VIG_2_DONE, 2}, { SDE_IRQ_TYPE_HIST_VIG_RSTSEQ, SSPP_VIG2, SDE_INTR_HIST_VIG_2_RSTSEQ_DONE, 2}, { SDE_IRQ_TYPE_HIST_VIG_DONE, SSPP_VIG3, SDE_INTR_HIST_VIG_3_DONE, 2}, { SDE_IRQ_TYPE_HIST_VIG_RSTSEQ, SSPP_VIG3, SDE_INTR_HIST_VIG_3_RSTSEQ_DONE, 2}, - /* irq_idx: 72-75 */ + /* irq_idx: 76-79 */ { SDE_IRQ_TYPE_HIST_DSPP_DONE, DSPP_0, SDE_INTR_HIST_DSPP_0_DONE, 2}, { SDE_IRQ_TYPE_HIST_DSPP_RSTSEQ, DSPP_0, SDE_INTR_HIST_DSPP_0_RSTSEQ_DONE, 2}, { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, - /* irq_idx: 76-79 */ + /* irq_idx: 80-83 */ { SDE_IRQ_TYPE_HIST_DSPP_DONE, DSPP_1, SDE_INTR_HIST_DSPP_1_DONE, 2}, { SDE_IRQ_TYPE_HIST_DSPP_RSTSEQ, DSPP_1, SDE_INTR_HIST_DSPP_1_RSTSEQ_DONE, 2}, { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, - /* irq_idx: 80-83 */ + /* irq_idx: 84-87 */ { SDE_IRQ_TYPE_HIST_DSPP_DONE, DSPP_2, SDE_INTR_HIST_DSPP_2_DONE, 2}, { SDE_IRQ_TYPE_HIST_DSPP_RSTSEQ, DSPP_2, SDE_INTR_HIST_DSPP_2_RSTSEQ_DONE, 2}, { SDE_IRQ_TYPE_HIST_DSPP_DONE, DSPP_3, SDE_INTR_HIST_DSPP_3_DONE, 2}, { SDE_IRQ_TYPE_HIST_DSPP_RSTSEQ, DSPP_3, SDE_INTR_HIST_DSPP_3_RSTSEQ_DONE, 2}, - /* irq_idx: 84-87 */ - { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, - { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, - { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, - { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, /* irq_idx: 88-91 */ { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, { SDE_IRQ_TYPE_RESERVED, 0, 0, 2}, @@ -905,7 +900,7 @@ static u32 sde_hw_intr_get_interrupt_status(struct sde_hw_intr *intr, sde_intr_set[reg_idx].status_off) & sde_irq_map[irq_idx].irq_mask; if (intr_status && clear) - SDE_REG_WRITE(&intr->hw, sde_intr_set[irq_idx].clr_off, + SDE_REG_WRITE(&intr->hw, sde_intr_set[reg_idx].clr_off, intr_status); spin_unlock_irqrestore(&intr->mask_lock, irq_flags); diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h index dcba248d27b0..2592fe26cb38 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -41,13 +41,27 @@ #define SDE_MAX_DE_CURVES 3 #endif -#define SDE_FORMAT_FLAG_YUV (1 << 0) -#define SDE_FORMAT_FLAG_DX (1 << 1) +enum sde_format_flags { + SDE_FORMAT_FLAG_YUV_BIT, + SDE_FORMAT_FLAG_DX_BIT, + SDE_FORMAT_FLAG_COMPRESSED_BIT, + SDE_FORMAT_FLAG_BIT_MAX, +}; -#define SDE_FORMAT_IS_YUV(X) ((X)->flag & SDE_FORMAT_FLAG_YUV) -#define SDE_FORMAT_IS_DX(X) ((X)->flag & SDE_FORMAT_FLAG_DX) +#define SDE_FORMAT_FLAG_YUV BIT(SDE_FORMAT_FLAG_YUV_BIT) +#define SDE_FORMAT_FLAG_DX BIT(SDE_FORMAT_FLAG_DX_BIT) +#define SDE_FORMAT_FLAG_COMPRESSED BIT(SDE_FORMAT_FLAG_COMPRESSED_BIT) +#define SDE_FORMAT_IS_YUV(X) \ + (test_bit(SDE_FORMAT_FLAG_YUV_BIT, (X)->flag)) +#define SDE_FORMAT_IS_DX(X) \ + (test_bit(SDE_FORMAT_FLAG_DX_BIT, (X)->flag)) #define SDE_FORMAT_IS_LINEAR(X) ((X)->fetch_mode == SDE_FETCH_LINEAR) -#define SDE_FORMAT_IS_UBWC(X) ((X)->fetch_mode == SDE_FETCH_UBWC) +#define SDE_FORMAT_IS_TILE(X) \ + (((X)->fetch_mode == SDE_FETCH_UBWC) && \ + !test_bit(SDE_FORMAT_FLAG_COMPRESSED_BIT, (X)->flag)) +#define SDE_FORMAT_IS_UBWC(X) \ + (((X)->fetch_mode == SDE_FETCH_UBWC) && \ + test_bit(SDE_FORMAT_FLAG_COMPRESSED_BIT, (X)->flag)) #define SDE_BLEND_FG_ALPHA_FG_CONST (0 << 0) #define SDE_BLEND_FG_ALPHA_BG_CONST (1 << 0) @@ -357,7 +371,7 @@ struct sde_format { u8 alpha_enable; u8 num_planes; enum sde_fetch_type fetch_mode; - u32 flag; + DECLARE_BITMAP(flag, SDE_FORMAT_FLAG_BIT_MAX); u16 tile_width; u16 tile_height; }; diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c index 882a1c84e9a2..ea2890d776ae 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -755,14 +755,17 @@ static void sde_hw_sspp_setup_csc(struct sde_hw_pipe *ctx, struct sde_csc_cfg *data) { u32 idx; + bool csc10 = false; if (_sspp_subblk_offset(ctx, SDE_SSPP_CSC, &idx) || !data) return; - if (test_bit(SDE_SSPP_CSC_10BIT, &ctx->cap->features)) + if (test_bit(SDE_SSPP_CSC_10BIT, &ctx->cap->features)) { idx += CSC_10BIT_OFFSET; + csc10 = true; + } - sde_hw_csc_setup(&ctx->hw, idx, data); + sde_hw_csc_setup(&ctx->hw, idx, data, csc10); } static void sde_hw_sspp_setup_sharpening(struct sde_hw_pipe *ctx, diff --git a/drivers/gpu/drm/msm/sde/sde_hw_util.c b/drivers/gpu/drm/msm/sde/sde_hw_util.c index 6f52f31a7569..b899f0c2f71c 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_util.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_util.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -42,9 +42,10 @@ u32 *sde_hw_util_get_log_mask_ptr(void) void sde_hw_csc_setup(struct sde_hw_blk_reg_map *c, u32 csc_reg_off, - struct sde_csc_cfg *data) + struct sde_csc_cfg *data, bool csc10) { static const u32 matrix_shift = 7; + u32 clamp_shift = csc10 ? 16 : 8; u32 val; /* matrix coeff - convert S15.16 to S4.9 */ @@ -64,19 +65,19 @@ void sde_hw_csc_setup(struct sde_hw_blk_reg_map *c, SDE_REG_WRITE(c, csc_reg_off + 0x10, val); /* Pre clamp */ - val = (data->csc_pre_lv[0] << 8) | data->csc_pre_lv[1]; + val = (data->csc_pre_lv[0] << clamp_shift) | data->csc_pre_lv[1]; SDE_REG_WRITE(c, csc_reg_off + 0x14, val); - val = (data->csc_pre_lv[2] << 8) | data->csc_pre_lv[3]; + val = (data->csc_pre_lv[2] << clamp_shift) | data->csc_pre_lv[3]; SDE_REG_WRITE(c, csc_reg_off + 0x18, val); - val = (data->csc_pre_lv[4] << 8) | data->csc_pre_lv[5]; + val = (data->csc_pre_lv[4] << clamp_shift) | data->csc_pre_lv[5]; SDE_REG_WRITE(c, csc_reg_off + 0x1c, val); /* Post clamp */ - val = (data->csc_post_lv[0] << 8) | data->csc_post_lv[1]; + val = (data->csc_post_lv[0] << clamp_shift) | data->csc_post_lv[1]; SDE_REG_WRITE(c, csc_reg_off + 0x20, val); - val = (data->csc_post_lv[2] << 8) | data->csc_post_lv[3]; + val = (data->csc_post_lv[2] << clamp_shift) | data->csc_post_lv[3]; SDE_REG_WRITE(c, csc_reg_off + 0x24, val); - val = (data->csc_post_lv[4] << 8) | data->csc_post_lv[5]; + val = (data->csc_post_lv[4] << clamp_shift) | data->csc_post_lv[5]; SDE_REG_WRITE(c, csc_reg_off + 0x28, val); /* Pre-Bias */ diff --git a/drivers/gpu/drm/msm/sde/sde_hw_util.h b/drivers/gpu/drm/msm/sde/sde_hw_util.h index a4d8be9de907..c38c22237a57 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_util.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -49,7 +49,7 @@ void *sde_hw_util_get_dir(void); void sde_hw_csc_setup(struct sde_hw_blk_reg_map *c, u32 csc_reg_off, - struct sde_csc_cfg *data); + struct sde_csc_cfg *data, bool csc10); #endif /* _SDE_HW_UTIL_H */ diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index 709c9970b357..031493aa42b8 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -601,6 +601,8 @@ static int _sde_kms_setup_displays(struct drm_device *dev, .mode_valid = sde_hdmi_mode_valid, .get_info = sde_hdmi_get_info, .set_property = sde_hdmi_set_property, + .get_property = sde_hdmi_get_property, + .pre_kickoff = sde_hdmi_pre_kickoff, }; struct msm_display_info info = {0}; struct drm_encoder *encoder; @@ -797,6 +799,16 @@ static void _sde_kms_drm_obj_destroy(struct sde_kms *sde_kms) _sde_kms_release_displays(sde_kms); } +static inline int sde_get_crtc_id(const char *display_type) +{ + if (!strcmp(display_type, "primary")) + return 0; + else if (!strcmp(display_type, "secondary")) + return 1; + else + return 2; +} + static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms) { struct drm_device *dev; @@ -829,28 +841,57 @@ static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms) (void)_sde_kms_setup_displays(dev, priv, sde_kms); max_crtc_count = min(catalog->mixer_count, priv->num_encoders); - max_plane_count = min_t(u32, catalog->sspp_count, MAX_PLANES); /* Create the planes */ primary_planes_idx = 0; - for (i = 0; i < max_plane_count; i++) { - bool primary = true; - - if (catalog->sspp[i].features & BIT(SDE_SSPP_CURSOR) - || primary_planes_idx >= max_crtc_count) - primary = false; - - plane = sde_plane_init(dev, catalog->sspp[i].id, primary, - (1UL << max_crtc_count) - 1); - if (IS_ERR(plane)) { - SDE_ERROR("sde_plane_init failed\n"); - ret = PTR_ERR(plane); - goto fail; + if (catalog->vp_count) { + max_plane_count = min_t(u32, catalog->vp_count, MAX_PLANES); + + for (i = 0; i < max_plane_count; i++) { + bool primary = true; + int crtc_id = + sde_get_crtc_id(catalog->vp[i].display_type); + + if (strcmp(catalog->vp[i].plane_type, "primary")) + primary = false; + + plane = sde_plane_init(dev, catalog->vp[i].id, + primary, 1UL << crtc_id, true); + if (IS_ERR(plane)) { + SDE_ERROR("sde_plane_init failed\n"); + ret = PTR_ERR(plane); + goto fail; + } + priv->planes[priv->num_planes++] = plane; + + if (primary) { + primary_planes[crtc_id] = plane; + primary_planes_idx++; + } + } + } else { + max_plane_count = min_t(u32, catalog->sspp_count, MAX_PLANES); + + for (i = 0; i < max_plane_count; i++) { + bool primary = true; + + if (catalog->sspp[i].features & BIT(SDE_SSPP_CURSOR) + || primary_planes_idx >= max_crtc_count) + primary = false; + + plane = sde_plane_init(dev, catalog->sspp[i].id, + primary, (1UL << max_crtc_count) - 1, + false); + if (IS_ERR(plane)) { + SDE_ERROR("sde_plane_init failed\n"); + ret = PTR_ERR(plane); + goto fail; + } + priv->planes[priv->num_planes++] = plane; + + if (primary) + primary_planes[primary_planes_idx++] = plane; } - priv->planes[priv->num_planes++] = plane; - - if (primary) - primary_planes[primary_planes_idx++] = plane; } max_crtc_count = min(max_crtc_count, primary_planes_idx); diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c index 114acfd7a173..85ebff08761b 100644 --- a/drivers/gpu/drm/msm/sde/sde_plane.c +++ b/drivers/gpu/drm/msm/sde/sde_plane.c @@ -78,20 +78,22 @@ enum sde_plane_qos { }; /* - * struct sde_plane - local sde plane structure + * struct sde_phy_plane - physical plane structure + * @sde_plane: Points to virtual plane + * @phy_plane_list: list of hw pipe(physical plane) + * @index: index of physical plane (starts from 0, order from left to right) + * @features: capabilities from catalog * @csc_cfg: Decoded user configuration for csc * @csc_usr_ptr: Points to csc_cfg if valid user config available * @csc_ptr: Points to sde_csc_cfg structure to use for current */ -struct sde_plane { - struct drm_plane base; - - struct msm_gem_address_space *aspace; - - struct mutex lock; - +struct sde_phy_plane { + struct sde_plane *sde_plane; + struct list_head phy_plane_list; enum sde_sspp pipe; - uint32_t features; /* capabilities from catalog */ + uint32_t index; + + uint32_t features; uint32_t nformats; uint32_t formats[64]; @@ -101,7 +103,6 @@ struct sde_plane { struct sde_hw_scaler3_cfg *scaler3_cfg; struct sde_hw_pipe_qos_cfg pipe_qos_cfg; uint32_t color_fill; - bool is_error; bool is_rt_pipe; struct sde_hw_pixel_ext pixel_ext; @@ -112,9 +113,22 @@ struct sde_plane { struct sde_csc_cfg *csc_ptr; const struct sde_sspp_sub_blks *pipe_sblk; +}; +/* + * struct sde_plane - local sde plane structure + */ +struct sde_plane { + struct drm_plane base; + + struct msm_gem_address_space *aspace; + struct mutex lock; + bool is_error; char pipe_name[SDE_NAME_SIZE]; + struct list_head phy_plane_head; + u32 num_of_phy_planes; + struct msm_property_info property_info; struct msm_property_data property_data[PLANE_PROP_COUNT]; struct drm_property_blob *blob_info; @@ -133,6 +147,20 @@ static bool sde_plane_enabled(struct drm_plane_state *state) return state && state->fb && state->crtc; } +static struct sde_kms *_sde_plane_get_kms(struct drm_plane *plane) +{ + struct msm_drm_private *priv; + + if (!plane || !plane->dev) + return NULL; + + priv = plane->dev->dev_private; + if (!priv) + return NULL; + + return to_sde_kms(priv->kms); +} + /** * _sde_plane_calc_fill_level - calculate fill level of the given source format * @plane: Pointer to drm plane @@ -140,20 +168,20 @@ static bool sde_plane_enabled(struct drm_plane_state *state) * @src_wdith: width of source buffer * Return: fill level corresponding to the source buffer/format or 0 if error */ -static inline int _sde_plane_calc_fill_level(struct drm_plane *plane, +static inline int _sde_plane_calc_fill_level(struct sde_phy_plane *pp, const struct sde_format *fmt, u32 src_width) { struct sde_plane *psde; u32 fixed_buff_size; u32 total_fl; - if (!plane || !fmt) { + if (!pp || !fmt) { SDE_ERROR("invalid arguments\n"); return 0; } - psde = to_sde_plane(plane); - fixed_buff_size = psde->pipe_sblk->pixel_ram_size; + psde = pp->sde_plane; + fixed_buff_size = pp->pipe_sblk->pixel_ram_size; if (fmt->fetch_planes == SDE_PLANE_PSEUDO_PLANAR) { if (fmt->chroma_sample == SDE_CHROMA_420) { @@ -171,7 +199,7 @@ static inline int _sde_plane_calc_fill_level(struct drm_plane *plane, } SDE_DEBUG("plane%u: pnum:%d fmt:%x w:%u fl:%u\n", - plane->base.id, psde->pipe - SSPP_VIG0, + psde->base.base.id, pp->pipe - SSPP_VIG0, fmt->base.pixel_format, src_width, total_fl); return total_fl; @@ -236,7 +264,7 @@ static inline u32 _sde_plane_get_qos_lut_macrotile(u32 total_fl) * @plane: Pointer to drm plane * @fb: Pointer to framebuffer associated with the given plane */ -static void _sde_plane_set_qos_lut(struct drm_plane *plane, +static void _sde_plane_set_qos_lut(struct sde_phy_plane *pp, struct drm_framebuffer *fb) { struct sde_plane *psde; @@ -244,30 +272,30 @@ static void _sde_plane_set_qos_lut(struct drm_plane *plane, u32 qos_lut; u32 total_fl = 0; - if (!plane || !fb) { - SDE_ERROR("invalid arguments plane %d fb %d\n", - plane != 0, fb != 0); + if (!pp || !fb) { + SDE_ERROR("invalid arguments phy_plane %d fb %d\n", + pp != NULL, fb != NULL); return; } - psde = to_sde_plane(plane); + psde = pp->sde_plane; - if (!psde->pipe_hw || !psde->pipe_sblk) { + if (!pp->pipe_hw || !pp->pipe_sblk) { SDE_ERROR("invalid arguments\n"); return; - } else if (!psde->pipe_hw->ops.setup_creq_lut) { + } else if (!pp->pipe_hw->ops.setup_creq_lut) { return; } - if (!psde->is_rt_pipe) { - qos_lut = psde->pipe_sblk->creq_lut_nrt; + if (!pp->is_rt_pipe) { + qos_lut = pp->pipe_sblk->creq_lut_nrt; } else { fmt = sde_get_sde_format_ext( fb->pixel_format, fb->modifier, drm_format_num_planes(fb->pixel_format)); - total_fl = _sde_plane_calc_fill_level(plane, fmt, - psde->pipe_cfg.src_rect.w); + total_fl = _sde_plane_calc_fill_level(pp, fmt, + pp->pipe_cfg.src_rect.w); if (SDE_FORMAT_IS_LINEAR(fmt)) qos_lut = _sde_plane_get_qos_lut_linear(total_fl); @@ -275,20 +303,20 @@ static void _sde_plane_set_qos_lut(struct drm_plane *plane, qos_lut = _sde_plane_get_qos_lut_macrotile(total_fl); } - psde->pipe_qos_cfg.creq_lut = qos_lut; + pp->pipe_qos_cfg.creq_lut = qos_lut; - trace_sde_perf_set_qos_luts(psde->pipe - SSPP_VIG0, + trace_sde_perf_set_qos_luts(pp->pipe - SSPP_VIG0, (fmt) ? fmt->base.pixel_format : 0, - psde->is_rt_pipe, total_fl, qos_lut, + pp->is_rt_pipe, total_fl, qos_lut, (fmt) ? SDE_FORMAT_IS_LINEAR(fmt) : 0); SDE_DEBUG("plane%u: pnum:%d fmt:%x rt:%d fl:%u lut:0x%x\n", - plane->base.id, - psde->pipe - SSPP_VIG0, + psde->base.base.id, + pp->pipe - SSPP_VIG0, (fmt) ? fmt->base.pixel_format : 0, - psde->is_rt_pipe, total_fl, qos_lut); + pp->is_rt_pipe, total_fl, qos_lut); - psde->pipe_hw->ops.setup_creq_lut(psde->pipe_hw, &psde->pipe_qos_cfg); + pp->pipe_hw->ops.setup_creq_lut(pp->pipe_hw, &pp->pipe_qos_cfg); } /** @@ -296,30 +324,30 @@ static void _sde_plane_set_qos_lut(struct drm_plane *plane, * @plane: Pointer to drm plane * @fb: Pointer to framebuffer associated with the given plane */ -static void _sde_plane_set_danger_lut(struct drm_plane *plane, +static void _sde_plane_set_danger_lut(struct sde_phy_plane *pp, struct drm_framebuffer *fb) { struct sde_plane *psde; const struct sde_format *fmt = NULL; u32 danger_lut, safe_lut; - if (!plane || !fb) { + if (!pp || !fb) { SDE_ERROR("invalid arguments\n"); return; } - psde = to_sde_plane(plane); + psde = pp->sde_plane; - if (!psde->pipe_hw || !psde->pipe_sblk) { + if (!pp->pipe_hw || !pp->pipe_sblk) { SDE_ERROR("invalid arguments\n"); return; - } else if (!psde->pipe_hw->ops.setup_danger_safe_lut) { + } else if (!pp->pipe_hw->ops.setup_danger_safe_lut) { return; } - if (!psde->is_rt_pipe) { - danger_lut = psde->pipe_sblk->danger_lut_nrt; - safe_lut = psde->pipe_sblk->safe_lut_nrt; + if (!pp->is_rt_pipe) { + danger_lut = pp->pipe_sblk->danger_lut_nrt; + safe_lut = pp->pipe_sblk->safe_lut_nrt; } else { fmt = sde_get_sde_format_ext( fb->pixel_format, @@ -327,33 +355,33 @@ static void _sde_plane_set_danger_lut(struct drm_plane *plane, drm_format_num_planes(fb->pixel_format)); if (SDE_FORMAT_IS_LINEAR(fmt)) { - danger_lut = psde->pipe_sblk->danger_lut_linear; - safe_lut = psde->pipe_sblk->safe_lut_linear; + danger_lut = pp->pipe_sblk->danger_lut_linear; + safe_lut = pp->pipe_sblk->safe_lut_linear; } else { - danger_lut = psde->pipe_sblk->danger_lut_tile; - safe_lut = psde->pipe_sblk->safe_lut_tile; + danger_lut = pp->pipe_sblk->danger_lut_tile; + safe_lut = pp->pipe_sblk->safe_lut_tile; } } - psde->pipe_qos_cfg.danger_lut = danger_lut; - psde->pipe_qos_cfg.safe_lut = safe_lut; + pp->pipe_qos_cfg.danger_lut = danger_lut; + pp->pipe_qos_cfg.safe_lut = safe_lut; - trace_sde_perf_set_danger_luts(psde->pipe - SSPP_VIG0, + trace_sde_perf_set_danger_luts(pp->pipe - SSPP_VIG0, (fmt) ? fmt->base.pixel_format : 0, (fmt) ? fmt->fetch_mode : 0, - psde->pipe_qos_cfg.danger_lut, - psde->pipe_qos_cfg.safe_lut); + pp->pipe_qos_cfg.danger_lut, + pp->pipe_qos_cfg.safe_lut); SDE_DEBUG("plane%u: pnum:%d fmt:%x mode:%d luts[0x%x, 0x%x]\n", - plane->base.id, - psde->pipe - SSPP_VIG0, + psde->base.base.id, + pp->pipe - SSPP_VIG0, fmt ? fmt->base.pixel_format : 0, fmt ? fmt->fetch_mode : -1, - psde->pipe_qos_cfg.danger_lut, - psde->pipe_qos_cfg.safe_lut); + pp->pipe_qos_cfg.danger_lut, + pp->pipe_qos_cfg.safe_lut); - psde->pipe_hw->ops.setup_danger_safe_lut(psde->pipe_hw, - &psde->pipe_qos_cfg); + pp->pipe_hw->ops.setup_danger_safe_lut(pp->pipe_hw, + &pp->pipe_qos_cfg); } /** @@ -362,85 +390,90 @@ static void _sde_plane_set_danger_lut(struct drm_plane *plane, * @enable: true to enable QoS control * @flags: QoS control mode (enum sde_plane_qos) */ -static void _sde_plane_set_qos_ctrl(struct drm_plane *plane, +static void _sde_plane_set_qos_ctrl(struct sde_phy_plane *pp, bool enable, u32 flags) { struct sde_plane *psde; - if (!plane) { + if (!pp) { SDE_ERROR("invalid arguments\n"); return; } - psde = to_sde_plane(plane); + psde = pp->sde_plane; - if (!psde->pipe_hw || !psde->pipe_sblk) { + if (!pp->pipe_hw || !pp->pipe_sblk) { SDE_ERROR("invalid arguments\n"); return; - } else if (!psde->pipe_hw->ops.setup_qos_ctrl) { + } else if (!pp->pipe_hw->ops.setup_qos_ctrl) { return; } if (flags & SDE_PLANE_QOS_VBLANK_CTRL) { - psde->pipe_qos_cfg.creq_vblank = psde->pipe_sblk->creq_vblank; - psde->pipe_qos_cfg.danger_vblank = - psde->pipe_sblk->danger_vblank; - psde->pipe_qos_cfg.vblank_en = enable; + pp->pipe_qos_cfg.creq_vblank = pp->pipe_sblk->creq_vblank; + pp->pipe_qos_cfg.danger_vblank = + pp->pipe_sblk->danger_vblank; + pp->pipe_qos_cfg.vblank_en = enable; } if (flags & SDE_PLANE_QOS_VBLANK_AMORTIZE) { /* this feature overrules previous VBLANK_CTRL */ - psde->pipe_qos_cfg.vblank_en = false; - psde->pipe_qos_cfg.creq_vblank = 0; /* clear vblank bits */ + pp->pipe_qos_cfg.vblank_en = false; + pp->pipe_qos_cfg.creq_vblank = 0; /* clear vblank bits */ } if (flags & SDE_PLANE_QOS_PANIC_CTRL) - psde->pipe_qos_cfg.danger_safe_en = enable; + pp->pipe_qos_cfg.danger_safe_en = enable; - if (!psde->is_rt_pipe) { - psde->pipe_qos_cfg.vblank_en = false; - psde->pipe_qos_cfg.danger_safe_en = false; + if (!pp->is_rt_pipe) { + pp->pipe_qos_cfg.vblank_en = false; + pp->pipe_qos_cfg.danger_safe_en = false; } SDE_DEBUG("plane%u: pnum:%d ds:%d vb:%d pri[0x%x, 0x%x] is_rt:%d\n", - plane->base.id, - psde->pipe - SSPP_VIG0, - psde->pipe_qos_cfg.danger_safe_en, - psde->pipe_qos_cfg.vblank_en, - psde->pipe_qos_cfg.creq_vblank, - psde->pipe_qos_cfg.danger_vblank, - psde->is_rt_pipe); - - psde->pipe_hw->ops.setup_qos_ctrl(psde->pipe_hw, - &psde->pipe_qos_cfg); + psde->base.base.id, + pp->pipe - SSPP_VIG0, + pp->pipe_qos_cfg.danger_safe_en, + pp->pipe_qos_cfg.vblank_en, + pp->pipe_qos_cfg.creq_vblank, + pp->pipe_qos_cfg.danger_vblank, + pp->is_rt_pipe); + + pp->pipe_hw->ops.setup_qos_ctrl(pp->pipe_hw, + &pp->pipe_qos_cfg); } -int sde_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable) +static int sde_plane_danger_signal_ctrl(struct sde_phy_plane *pp, bool enable) { struct sde_plane *psde; struct msm_drm_private *priv; struct sde_kms *sde_kms; - if (!plane || !plane->dev) { + if (!pp) { SDE_ERROR("invalid arguments\n"); return -EINVAL; } + psde = pp->sde_plane; - priv = plane->dev->dev_private; + if (!psde->base.dev) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + priv = psde->base.dev->dev_private; if (!priv || !priv->kms) { SDE_ERROR("invalid KMS reference\n"); return -EINVAL; } sde_kms = to_sde_kms(priv->kms); - psde = to_sde_plane(plane); - if (!psde->is_rt_pipe) + if (!pp->is_rt_pipe) goto end; sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true); - _sde_plane_set_qos_ctrl(plane, enable, SDE_PLANE_QOS_PANIC_CTRL); + _sde_plane_set_qos_ctrl(pp, enable, SDE_PLANE_QOS_PANIC_CTRL); sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false); @@ -453,7 +486,7 @@ end: * @plane: Pointer to drm plane * @crtc: Pointer to drm crtc */ -static void _sde_plane_set_ot_limit(struct drm_plane *plane, +static void _sde_plane_set_ot_limit(struct sde_phy_plane *pp, struct drm_crtc *crtc) { struct sde_plane *psde; @@ -461,34 +494,38 @@ static void _sde_plane_set_ot_limit(struct drm_plane *plane, struct msm_drm_private *priv; struct sde_kms *sde_kms; - if (!plane || !plane->dev || !crtc) { - SDE_ERROR("invalid arguments plane %d crtc %d\n", - plane != 0, crtc != 0); + if (!pp || !crtc) { + SDE_ERROR("invalid arguments phy_plane %d crtc %d\n", + pp != NULL, crtc != NULL); + return; + } + psde = pp->sde_plane; + if (!psde->base.dev) { + SDE_ERROR("invalid DRM device\n"); return; } - priv = plane->dev->dev_private; + priv = psde->base.dev->dev_private; if (!priv || !priv->kms) { SDE_ERROR("invalid KMS reference\n"); return; } sde_kms = to_sde_kms(priv->kms); - psde = to_sde_plane(plane); - if (!psde->pipe_hw) { + if (!pp->pipe_hw) { SDE_ERROR("invalid pipe reference\n"); return; } memset(&ot_params, 0, sizeof(ot_params)); - ot_params.xin_id = psde->pipe_hw->cap->xin_id; - ot_params.num = psde->pipe_hw->idx - SSPP_NONE; - ot_params.width = psde->pipe_cfg.src_rect.w; - ot_params.height = psde->pipe_cfg.src_rect.h; - ot_params.is_wfd = !psde->is_rt_pipe; + ot_params.xin_id = pp->pipe_hw->cap->xin_id; + ot_params.num = pp->pipe_hw->idx - SSPP_NONE; + ot_params.width = pp->pipe_cfg.src_rect.w; + ot_params.height = pp->pipe_cfg.src_rect.h; + ot_params.is_wfd = !pp->is_rt_pipe; ot_params.frame_rate = crtc->mode.vrefresh; ot_params.vbif_idx = VBIF_RT; - ot_params.clk_ctrl = psde->pipe_hw->cap->clk_ctrl; + ot_params.clk_ctrl = pp->pipe_hw->cap->clk_ctrl; ot_params.rd = true; sde_vbif_set_ot_limit(sde_kms, &ot_params); @@ -559,40 +596,97 @@ int sde_plane_wait_input_fence(struct drm_plane *plane, uint32_t wait_ms) return ret; } -static inline void _sde_plane_set_scanout(struct drm_plane *plane, +/** + * _sde_plane_get_aspace: gets the address space based on the + * fb_translation mode property + */ +static int _sde_plane_get_aspace( + struct sde_plane *psde, + struct sde_plane_state *pstate, + struct msm_gem_address_space **aspace) +{ + struct sde_kms *kms; + int mode; + + if (!psde || !pstate || !aspace) { + SDE_ERROR("invalid parameters\n"); + return -EINVAL; + } + + kms = _sde_plane_get_kms(&psde->base); + if (!kms) { + SDE_ERROR("invalid kms\n"); + return -EINVAL; + } + + mode = sde_plane_get_property(pstate, + PLANE_PROP_FB_TRANSLATION_MODE); + + switch (mode) { + case SDE_DRM_FB_NON_SEC: + *aspace = kms->aspace[MSM_SMMU_DOMAIN_UNSECURE]; + if (!aspace) + return -EINVAL; + break; + case SDE_DRM_FB_SEC: + *aspace = kms->aspace[MSM_SMMU_DOMAIN_SECURE]; + if (!aspace) + return -EINVAL; + break; + case SDE_DRM_FB_SEC_DIR_TRANS: + case SDE_DRM_FB_NON_SEC_DIR_TRANS: + *aspace = NULL; + break; + default: + SDE_ERROR("invalid fb_translation mode:%d\n", mode); + return -EFAULT; + } + + return 0; +} + +static inline void _sde_plane_set_scanout(struct sde_phy_plane *pp, struct sde_plane_state *pstate, struct sde_hw_pipe_cfg *pipe_cfg, struct drm_framebuffer *fb) { struct sde_plane *psde; + struct msm_gem_address_space *aspace = NULL; int ret; - if (!plane || !pstate || !pipe_cfg || !fb) { + if (!pp || !pstate || !pipe_cfg || !fb) { SDE_ERROR( - "invalid arg(s), plane %d state %d cfg %d fb %d\n", - plane != 0, pstate != 0, pipe_cfg != 0, fb != 0); + "invalid arg(s), phy_plane %d state %d cfg %d fb %d\n", + pp != 0, pstate != 0, pipe_cfg != 0, fb != 0); return; } - psde = to_sde_plane(plane); - if (!psde->pipe_hw) { + psde = pp->sde_plane; + if (!pp->pipe_hw) { SDE_ERROR_PLANE(psde, "invalid pipe_hw\n"); return; } - ret = sde_format_populate_layout(psde->aspace, fb, &pipe_cfg->layout); + ret = _sde_plane_get_aspace(psde, pstate, &aspace); + if (ret) { + SDE_ERROR_PLANE(psde, "Failed to get aspace %d\n", ret); + return; + } + + ret = sde_format_populate_layout(aspace, fb, &pipe_cfg->layout); if (ret == -EAGAIN) SDE_DEBUG_PLANE(psde, "not updating same src addrs\n"); else if (ret) SDE_ERROR_PLANE(psde, "failed to get format layout, %d\n", ret); - else if (psde->pipe_hw->ops.setup_sourceaddress) - psde->pipe_hw->ops.setup_sourceaddress(psde->pipe_hw, pipe_cfg); + else if (pp->pipe_hw && pp->pipe_hw->ops.setup_sourceaddress) + pp->pipe_hw->ops.setup_sourceaddress(pp->pipe_hw, pipe_cfg); } -static int _sde_plane_setup_scaler3_lut(struct sde_plane *psde, +static int _sde_plane_setup_scaler3_lut(struct sde_phy_plane *pp, struct sde_plane_state *pstate) { - struct sde_hw_scaler3_cfg *cfg = psde->scaler3_cfg; + struct sde_plane *psde = pp->sde_plane; + struct sde_hw_scaler3_cfg *cfg = pp->scaler3_cfg; int ret = 0; cfg->dir_lut = msm_property_get_blob( @@ -612,7 +706,7 @@ static int _sde_plane_setup_scaler3_lut(struct sde_plane *psde, return ret; } -static void _sde_plane_setup_scaler3(struct sde_plane *psde, +static void _sde_plane_setup_scaler3(struct sde_phy_plane *pp, uint32_t src_w, uint32_t src_h, uint32_t dst_w, uint32_t dst_h, struct sde_hw_scaler3_cfg *scale_cfg, const struct sde_format *fmt, @@ -620,10 +714,10 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde, { uint32_t decimated, i; - if (!psde || !scale_cfg || !fmt || !chroma_subsmpl_h || + if (!pp || !scale_cfg || !fmt || !chroma_subsmpl_h || !chroma_subsmpl_v) { SDE_ERROR("psde %pK scale_cfg %pK fmt %pK smp_h %d smp_v %d\n" - , psde, scale_cfg, fmt, chroma_subsmpl_h, + , pp, scale_cfg, fmt, chroma_subsmpl_h, chroma_subsmpl_v); return; } @@ -631,11 +725,11 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde, memset(scale_cfg, 0, sizeof(*scale_cfg)); decimated = DECIMATED_DIMENSION(src_w, - psde->pipe_cfg.horz_decimation); + pp->pipe_cfg.horz_decimation); scale_cfg->phase_step_x[SDE_SSPP_COMP_0] = mult_frac((1 << PHASE_STEP_SHIFT), decimated, dst_w); decimated = DECIMATED_DIMENSION(src_h, - psde->pipe_cfg.vert_decimation); + pp->pipe_cfg.vert_decimation); scale_cfg->phase_step_y[SDE_SSPP_COMP_0] = mult_frac((1 << PHASE_STEP_SHIFT), decimated, dst_h); @@ -657,9 +751,9 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde, for (i = 0; i < SDE_MAX_PLANES; i++) { scale_cfg->src_width[i] = DECIMATED_DIMENSION(src_w, - psde->pipe_cfg.horz_decimation); + pp->pipe_cfg.horz_decimation); scale_cfg->src_height[i] = DECIMATED_DIMENSION(src_h, - psde->pipe_cfg.vert_decimation); + pp->pipe_cfg.vert_decimation); if (SDE_FORMAT_IS_YUV(fmt)) scale_cfg->src_width[i] &= ~0x1; if (i == SDE_SSPP_COMP_1_2 || i == SDE_SSPP_COMP_2) { @@ -668,9 +762,9 @@ static void _sde_plane_setup_scaler3(struct sde_plane *psde, } scale_cfg->preload_x[i] = SDE_QSEED3_DEFAULT_PRELOAD_H; scale_cfg->preload_y[i] = SDE_QSEED3_DEFAULT_PRELOAD_V; - psde->pixel_ext.num_ext_pxls_top[i] = + pp->pixel_ext.num_ext_pxls_top[i] = scale_cfg->src_height[i]; - psde->pixel_ext.num_ext_pxls_left[i] = + pp->pixel_ext.num_ext_pxls_left[i] = scale_cfg->src_width[i]; } if (!(SDE_FORMAT_IS_YUV(fmt)) && (src_h == dst_h) @@ -835,7 +929,7 @@ static void _sde_plane_setup_pixel_ext(struct sde_plane *psde, } } -static inline void _sde_plane_setup_csc(struct sde_plane *psde) +static inline void _sde_plane_setup_csc(struct sde_phy_plane *pp) { static const struct sde_csc_cfg sde_csc_YUV2RGB_601L = { { @@ -866,26 +960,30 @@ static inline void _sde_plane_setup_csc(struct sde_plane *psde) { 0x00, 0x3ff, 0x00, 0x3ff, 0x00, 0x3ff,}, }; - if (!psde) { + struct sde_plane *psde; + + if (!pp) { SDE_ERROR("invalid plane\n"); return; } + psde = pp->sde_plane; /* revert to kernel default if override not available */ - if (psde->csc_usr_ptr) - psde->csc_ptr = psde->csc_usr_ptr; - else if (BIT(SDE_SSPP_CSC_10BIT) & psde->features) - psde->csc_ptr = (struct sde_csc_cfg *)&sde_csc10_YUV2RGB_601L; + if (pp->csc_usr_ptr) + pp->csc_ptr = pp->csc_usr_ptr; + else if (BIT(SDE_SSPP_CSC_10BIT) & pp->features) + pp->csc_ptr = (struct sde_csc_cfg *)&sde_csc10_YUV2RGB_601L; else - psde->csc_ptr = (struct sde_csc_cfg *)&sde_csc_YUV2RGB_601L; + pp->csc_ptr = (struct sde_csc_cfg *)&sde_csc_YUV2RGB_601L; SDE_DEBUG_PLANE(psde, "using 0x%X 0x%X 0x%X...\n", - psde->csc_ptr->csc_mv[0], - psde->csc_ptr->csc_mv[1], - psde->csc_ptr->csc_mv[2]); + pp->csc_ptr->csc_mv[0], + pp->csc_ptr->csc_mv[1], + pp->csc_ptr->csc_mv[2]); } -static void sde_color_process_plane_setup(struct drm_plane *plane) +static void sde_color_process_plane_setup(struct drm_plane *plane, + struct sde_phy_plane *pp) { struct sde_plane *psde; struct sde_plane_state *pstate; @@ -893,32 +991,32 @@ static void sde_color_process_plane_setup(struct drm_plane *plane) struct drm_msm_memcol *memcol = NULL; size_t memcol_sz = 0; - psde = to_sde_plane(plane); + psde = pp->sde_plane; pstate = to_sde_plane_state(plane->state); hue = (uint32_t) sde_plane_get_property(pstate, PLANE_PROP_HUE_ADJUST); - if (psde->pipe_hw->ops.setup_pa_hue) - psde->pipe_hw->ops.setup_pa_hue(psde->pipe_hw, &hue); + if (pp->pipe_hw->ops.setup_pa_hue) + pp->pipe_hw->ops.setup_pa_hue(pp->pipe_hw, &hue); saturation = (uint32_t) sde_plane_get_property(pstate, PLANE_PROP_SATURATION_ADJUST); - if (psde->pipe_hw->ops.setup_pa_sat) - psde->pipe_hw->ops.setup_pa_sat(psde->pipe_hw, &saturation); + if (pp->pipe_hw->ops.setup_pa_sat) + pp->pipe_hw->ops.setup_pa_sat(pp->pipe_hw, &saturation); value = (uint32_t) sde_plane_get_property(pstate, PLANE_PROP_VALUE_ADJUST); - if (psde->pipe_hw->ops.setup_pa_val) - psde->pipe_hw->ops.setup_pa_val(psde->pipe_hw, &value); + if (pp->pipe_hw->ops.setup_pa_val) + pp->pipe_hw->ops.setup_pa_val(pp->pipe_hw, &value); contrast = (uint32_t) sde_plane_get_property(pstate, PLANE_PROP_CONTRAST_ADJUST); - if (psde->pipe_hw->ops.setup_pa_cont) - psde->pipe_hw->ops.setup_pa_cont(psde->pipe_hw, &contrast); + if (pp->pipe_hw->ops.setup_pa_cont) + pp->pipe_hw->ops.setup_pa_cont(pp->pipe_hw, &contrast); - if (psde->pipe_hw->ops.setup_pa_memcolor) { + if (pp->pipe_hw->ops.setup_pa_memcolor) { /* Skin memory color setup */ memcol = msm_property_get_blob(&psde->property_info, pstate->property_blobs, &memcol_sz, PLANE_PROP_SKIN_COLOR); - psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw, + pp->pipe_hw->ops.setup_pa_memcolor(pp->pipe_hw, MEMCOLOR_SKIN, memcol); /* Sky memory color setup */ @@ -926,7 +1024,7 @@ static void sde_color_process_plane_setup(struct drm_plane *plane) pstate->property_blobs, &memcol_sz, PLANE_PROP_SKY_COLOR); - psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw, + pp->pipe_hw->ops.setup_pa_memcolor(pp->pipe_hw, MEMCOLOR_SKY, memcol); /* Foliage memory color setup */ @@ -934,87 +1032,89 @@ static void sde_color_process_plane_setup(struct drm_plane *plane) pstate->property_blobs, &memcol_sz, PLANE_PROP_FOLIAGE_COLOR); - psde->pipe_hw->ops.setup_pa_memcolor(psde->pipe_hw, + pp->pipe_hw->ops.setup_pa_memcolor(pp->pipe_hw, MEMCOLOR_FOLIAGE, memcol); } } -static void _sde_plane_setup_scaler(struct sde_plane *psde, +static void _sde_plane_setup_scaler(struct sde_phy_plane *pp, const struct sde_format *fmt, struct sde_plane_state *pstate) { struct sde_hw_pixel_ext *pe; uint32_t chroma_subsmpl_h, chroma_subsmpl_v; + struct sde_plane *psde; - if (!psde || !fmt) { - SDE_ERROR("invalid arg(s), plane %d fmt %d state %d\n", - psde != 0, fmt != 0, pstate != 0); + if (!pp || !fmt || !pstate || !pp->sde_plane) { + SDE_ERROR("invalid arg(s), phy_plane %d fmt %d\n", + pp != NULL, fmt != NULL); return; } + psde = pp->sde_plane; - pe = &(psde->pixel_ext); + pe = &(pp->pixel_ext); - psde->pipe_cfg.horz_decimation = + pp->pipe_cfg.horz_decimation = sde_plane_get_property(pstate, PLANE_PROP_H_DECIMATE); - psde->pipe_cfg.vert_decimation = + pp->pipe_cfg.vert_decimation = sde_plane_get_property(pstate, PLANE_PROP_V_DECIMATE); /* don't chroma subsample if decimating */ - chroma_subsmpl_h = psde->pipe_cfg.horz_decimation ? 1 : + chroma_subsmpl_h = pp->pipe_cfg.horz_decimation ? 1 : drm_format_horz_chroma_subsampling(fmt->base.pixel_format); - chroma_subsmpl_v = psde->pipe_cfg.vert_decimation ? 1 : + chroma_subsmpl_v = pp->pipe_cfg.vert_decimation ? 1 : drm_format_vert_chroma_subsampling(fmt->base.pixel_format); /* update scaler */ - if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) { + if (pp->features & BIT(SDE_SSPP_SCALER_QSEED3)) { int error; - error = _sde_plane_setup_scaler3_lut(psde, pstate); - if (error || !psde->pixel_ext_usr) { + error = _sde_plane_setup_scaler3_lut(pp, pstate); + if (error || !pp->pixel_ext_usr) { memset(pe, 0, sizeof(struct sde_hw_pixel_ext)); /* calculate default config for QSEED3 */ - _sde_plane_setup_scaler3(psde, - psde->pipe_cfg.src_rect.w, - psde->pipe_cfg.src_rect.h, - psde->pipe_cfg.dst_rect.w, - psde->pipe_cfg.dst_rect.h, - psde->scaler3_cfg, fmt, + _sde_plane_setup_scaler3(pp, + pp->pipe_cfg.src_rect.w, + pp->pipe_cfg.src_rect.h, + pp->pipe_cfg.dst_rect.w, + pp->pipe_cfg.dst_rect.h, + pp->scaler3_cfg, fmt, chroma_subsmpl_h, chroma_subsmpl_v); } - } else if (!psde->pixel_ext_usr) { + } else if (!pp->pixel_ext_usr) { uint32_t deci_dim, i; /* calculate default configuration for QSEED2 */ memset(pe, 0, sizeof(struct sde_hw_pixel_ext)); SDE_DEBUG_PLANE(psde, "default config\n"); - deci_dim = DECIMATED_DIMENSION(psde->pipe_cfg.src_rect.w, - psde->pipe_cfg.horz_decimation); + deci_dim = DECIMATED_DIMENSION(pp->pipe_cfg.src_rect.w, + pp->pipe_cfg.horz_decimation); _sde_plane_setup_scaler2(psde, deci_dim, - psde->pipe_cfg.dst_rect.w, + pp->pipe_cfg.dst_rect.w, pe->phase_step_x, pe->horz_filter, fmt, chroma_subsmpl_h); if (SDE_FORMAT_IS_YUV(fmt)) deci_dim &= ~0x1; - _sde_plane_setup_pixel_ext(psde, psde->pipe_cfg.src_rect.w, - psde->pipe_cfg.dst_rect.w, deci_dim, + _sde_plane_setup_pixel_ext(psde, pp->pipe_cfg.src_rect.w, + pp->pipe_cfg.dst_rect.w, deci_dim, pe->phase_step_x, pe->roi_w, pe->num_ext_pxls_left, pe->num_ext_pxls_right, pe->horz_filter, fmt, chroma_subsmpl_h, 0); - deci_dim = DECIMATED_DIMENSION(psde->pipe_cfg.src_rect.h, - psde->pipe_cfg.vert_decimation); + deci_dim = DECIMATED_DIMENSION(pp->pipe_cfg.src_rect.h, + pp->pipe_cfg.vert_decimation); _sde_plane_setup_scaler2(psde, deci_dim, - psde->pipe_cfg.dst_rect.h, + pp->pipe_cfg.dst_rect.h, pe->phase_step_y, pe->vert_filter, fmt, chroma_subsmpl_v); - _sde_plane_setup_pixel_ext(psde, psde->pipe_cfg.src_rect.h, - psde->pipe_cfg.dst_rect.h, deci_dim, + _sde_plane_setup_pixel_ext(psde, pp->pipe_cfg.src_rect.h, + pp->pipe_cfg.dst_rect.h, deci_dim, pe->phase_step_y, pe->roi_h, pe->num_ext_pxls_top, @@ -1052,22 +1152,22 @@ static void _sde_plane_setup_scaler(struct sde_plane *psde, * @alpha: 8-bit fill alpha value, 255 selects 100% alpha * Returns: 0 on success */ -static int _sde_plane_color_fill(struct sde_plane *psde, +static int _sde_plane_color_fill(struct sde_phy_plane *pp, uint32_t color, uint32_t alpha) { const struct sde_format *fmt; - if (!psde) { + if (!pp) { SDE_ERROR("invalid plane\n"); return -EINVAL; } - if (!psde->pipe_hw) { - SDE_ERROR_PLANE(psde, "invalid plane h/w pointer\n"); + if (!pp->pipe_hw) { + SDE_ERROR_PLANE(pp->sde_plane, "invalid plane h/w pointer\n"); return -EINVAL; } - SDE_DEBUG_PLANE(psde, "\n"); + SDE_DEBUG_PLANE(pp->sde_plane, "\n"); /* * select fill format to match user property expectation, @@ -1076,35 +1176,35 @@ static int _sde_plane_color_fill(struct sde_plane *psde, fmt = sde_get_sde_format(DRM_FORMAT_ABGR8888); /* update sspp */ - if (fmt && psde->pipe_hw->ops.setup_solidfill) { - psde->pipe_hw->ops.setup_solidfill(psde->pipe_hw, + if (fmt && pp->pipe_hw->ops.setup_solidfill) { + pp->pipe_hw->ops.setup_solidfill(pp->pipe_hw, (color & 0xFFFFFF) | ((alpha & 0xFF) << 24)); /* override scaler/decimation if solid fill */ - psde->pipe_cfg.src_rect.x = 0; - psde->pipe_cfg.src_rect.y = 0; - psde->pipe_cfg.src_rect.w = psde->pipe_cfg.dst_rect.w; - psde->pipe_cfg.src_rect.h = psde->pipe_cfg.dst_rect.h; + pp->pipe_cfg.src_rect.x = 0; + pp->pipe_cfg.src_rect.y = 0; + pp->pipe_cfg.src_rect.w = pp->pipe_cfg.dst_rect.w; + pp->pipe_cfg.src_rect.h = pp->pipe_cfg.dst_rect.h; - _sde_plane_setup_scaler(psde, fmt, 0); + _sde_plane_setup_scaler(pp, fmt, NULL); - if (psde->pipe_hw->ops.setup_format) - psde->pipe_hw->ops.setup_format(psde->pipe_hw, + if (pp->pipe_hw->ops.setup_format) + pp->pipe_hw->ops.setup_format(pp->pipe_hw, fmt, SDE_SSPP_SOLID_FILL); - if (psde->pipe_hw->ops.setup_rects) - psde->pipe_hw->ops.setup_rects(psde->pipe_hw, - &psde->pipe_cfg, &psde->pixel_ext, - psde->scaler3_cfg); + if (pp->pipe_hw->ops.setup_rects) + pp->pipe_hw->ops.setup_rects(pp->pipe_hw, + &pp->pipe_cfg, &pp->pixel_ext, + pp->scaler3_cfg); } return 0; } static int _sde_plane_mode_set(struct drm_plane *plane, - struct drm_plane_state *state) + struct drm_plane_state *state) { - uint32_t nplanes, src_flags; + uint32_t nplanes, src_flags = 0x0; struct sde_plane *psde; struct sde_plane_state *pstate; const struct sde_format *fmt; @@ -1113,6 +1213,9 @@ static int _sde_plane_mode_set(struct drm_plane *plane, struct sde_rect src, dst; bool q16_data = true; int idx; + struct sde_phy_plane *pp; + uint32_t num_of_phy_planes = 0, maxlinewidth = 0xFFFF; + int mode = 0; if (!plane) { SDE_ERROR("invalid plane\n"); @@ -1170,18 +1273,32 @@ static int _sde_plane_mode_set(struct drm_plane *plane, } } - if (pstate->dirty & SDE_PLANE_DIRTY_RECTS) - memset(&(psde->pipe_cfg), 0, sizeof(struct sde_hw_pipe_cfg)); + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (pstate->dirty & SDE_PLANE_DIRTY_RECTS) + memset(&(pp->pipe_cfg), 0, + sizeof(struct sde_hw_pipe_cfg)); + + _sde_plane_set_scanout(pp, pstate, &pp->pipe_cfg, fb); + + pstate->pending = true; - _sde_plane_set_scanout(plane, pstate, &psde->pipe_cfg, fb); + pp->is_rt_pipe = sde_crtc_is_rt(crtc); + _sde_plane_set_qos_ctrl(pp, false, SDE_PLANE_QOS_PANIC_CTRL); + } /* early out if nothing dirty */ if (!pstate->dirty) return 0; - pstate->pending = true; - psde->is_rt_pipe = sde_crtc_is_rt(crtc); - _sde_plane_set_qos_ctrl(plane, false, SDE_PLANE_QOS_PANIC_CTRL); + memset(&src, 0, sizeof(struct sde_rect)); + + /* update secure session flag */ + mode = sde_plane_get_property(pstate, + PLANE_PROP_FB_TRANSLATION_MODE); + if ((mode == SDE_DRM_FB_SEC) || + (mode == SDE_DRM_FB_SEC_DIR_TRANS)) + src_flags |= SDE_SSPP_SECURE_OVERLAY_SESSION; + /* update roi config */ if (pstate->dirty & SDE_PLANE_DIRTY_RECTS) { @@ -1201,72 +1318,100 @@ static int _sde_plane_mode_set(struct drm_plane *plane, BIT(SDE_DRM_DEINTERLACE)) { SDE_DEBUG_PLANE(psde, "deinterlace\n"); for (idx = 0; idx < SDE_MAX_PLANES; ++idx) - psde->pipe_cfg.layout.plane_pitch[idx] <<= 1; + pp->pipe_cfg.layout.plane_pitch[idx] <<= 1; src.h /= 2; src.y = DIV_ROUND_UP(src.y, 2); src.y &= ~0x1; } + } + + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (maxlinewidth > pp->pipe_sblk->maxlinewidth) + maxlinewidth = pp->pipe_sblk->maxlinewidth; + num_of_phy_planes++; + } - psde->pipe_cfg.src_rect = src; - psde->pipe_cfg.dst_rect = dst; + /* + * Only need to use one physical plane if plane width is still within + * the limitation. + */ + if (maxlinewidth >= (src.x + src.w)) + num_of_phy_planes = 1; + + if (num_of_phy_planes > 1) { + /* Adjust width for multi-pipe */ + src.w /= num_of_phy_planes; + dst.w /= num_of_phy_planes; + } + + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + /* Adjust offset for multi-pipe */ + src.x += src.w * pp->index; + dst.x += dst.w * pp->index; + + pp->pipe_cfg.src_rect = src; + pp->pipe_cfg.dst_rect = dst; /* check for color fill */ - psde->color_fill = (uint32_t)sde_plane_get_property(pstate, + pp->color_fill = (uint32_t)sde_plane_get_property(pstate, PLANE_PROP_COLOR_FILL); - if (psde->color_fill & SDE_PLANE_COLOR_FILL_FLAG) { + if (pp->color_fill & SDE_PLANE_COLOR_FILL_FLAG) { /* skip remaining processing on color fill */ pstate->dirty = 0x0; - } else if (psde->pipe_hw->ops.setup_rects) { - _sde_plane_setup_scaler(psde, fmt, pstate); + } else if (pp->pipe_hw->ops.setup_rects) { + _sde_plane_setup_scaler(pp, fmt, pstate); - psde->pipe_hw->ops.setup_rects(psde->pipe_hw, - &psde->pipe_cfg, &psde->pixel_ext, - psde->scaler3_cfg); + pp->pipe_hw->ops.setup_rects(pp->pipe_hw, + &pp->pipe_cfg, &pp->pixel_ext, + pp->scaler3_cfg); } - } - if ((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) && - psde->pipe_hw->ops.setup_format) { - src_flags = 0x0; + if (((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) || + (src_flags & + SDE_SSPP_SECURE_OVERLAY_SESSION)) && + pp->pipe_hw->ops.setup_format) { SDE_DEBUG_PLANE(psde, "rotation 0x%llX\n", sde_plane_get_property(pstate, PLANE_PROP_ROTATION)); - if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION) & - BIT(DRM_REFLECT_X)) - src_flags |= SDE_SSPP_FLIP_LR; - if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION) & - BIT(DRM_REFLECT_Y)) - src_flags |= SDE_SSPP_FLIP_UD; - - /* update format */ - psde->pipe_hw->ops.setup_format(psde->pipe_hw, fmt, src_flags); - - /* update csc */ - if (SDE_FORMAT_IS_YUV(fmt)) - _sde_plane_setup_csc(psde); - else - psde->csc_ptr = 0; - } + if (sde_plane_get_property(pstate, PLANE_PROP_ROTATION) + & BIT(DRM_REFLECT_X)) + src_flags |= SDE_SSPP_FLIP_LR; + if (sde_plane_get_property(pstate, + PLANE_PROP_ROTATION) & BIT(DRM_REFLECT_Y)) + src_flags |= SDE_SSPP_FLIP_UD; + + /* update format */ + pp->pipe_hw->ops.setup_format(pp->pipe_hw, + fmt, src_flags); + + /* update csc */ + if (SDE_FORMAT_IS_YUV(fmt)) + _sde_plane_setup_csc(pp); + else + pp->csc_ptr = NULL; + } - sde_color_process_plane_setup(plane); + sde_color_process_plane_setup(plane, pp); - /* update sharpening */ - if ((pstate->dirty & SDE_PLANE_DIRTY_SHARPEN) && - psde->pipe_hw->ops.setup_sharpening) { - psde->sharp_cfg.strength = SHARP_STRENGTH_DEFAULT; - psde->sharp_cfg.edge_thr = SHARP_EDGE_THR_DEFAULT; - psde->sharp_cfg.smooth_thr = SHARP_SMOOTH_THR_DEFAULT; - psde->sharp_cfg.noise_thr = SHARP_NOISE_THR_DEFAULT; + /* update sharpening */ + if ((pstate->dirty & SDE_PLANE_DIRTY_SHARPEN) && + pp->pipe_hw->ops.setup_sharpening) { + pp->sharp_cfg.strength = SHARP_STRENGTH_DEFAULT; + pp->sharp_cfg.edge_thr = SHARP_EDGE_THR_DEFAULT; + pp->sharp_cfg.smooth_thr = SHARP_SMOOTH_THR_DEFAULT; + pp->sharp_cfg.noise_thr = SHARP_NOISE_THR_DEFAULT; - psde->pipe_hw->ops.setup_sharpening(psde->pipe_hw, - &psde->sharp_cfg); - } + pp->pipe_hw->ops.setup_sharpening(pp->pipe_hw, + &pp->sharp_cfg); + } - _sde_plane_set_qos_lut(plane, fb); - _sde_plane_set_danger_lut(plane, fb); + _sde_plane_set_qos_lut(pp, fb); + _sde_plane_set_danger_lut(pp, fb); - if (plane->type != DRM_PLANE_TYPE_CURSOR) { - _sde_plane_set_qos_ctrl(plane, true, SDE_PLANE_QOS_PANIC_CTRL); - _sde_plane_set_ot_limit(plane, crtc); + if (plane->type != DRM_PLANE_TYPE_CURSOR) { + _sde_plane_set_qos_ctrl(pp, true, + SDE_PLANE_QOS_PANIC_CTRL); + _sde_plane_set_ot_limit(pp, crtc); + } } /* clear dirty */ @@ -1280,10 +1425,23 @@ static int sde_plane_prepare_fb(struct drm_plane *plane, { struct drm_framebuffer *fb = new_state->fb; struct sde_plane *psde = to_sde_plane(plane); + struct sde_plane_state *pstate; + int rc; + + if (!psde || !new_state) + return -EINVAL; if (!new_state->fb) return 0; + pstate = to_sde_plane_state(new_state); + rc = _sde_plane_get_aspace(psde, pstate, &psde->aspace); + + if (rc) { + SDE_ERROR_PLANE(psde, "Failed to get aspace %d\n", rc); + return rc; + } + SDE_DEBUG_PLANE(psde, "FB[%u]\n", fb->base.id); return msm_framebuffer_prepare(fb, psde->aspace); } @@ -1393,10 +1551,12 @@ static int sde_plane_atomic_check(struct drm_plane *plane, uint32_t deci_w, deci_h, src_deci_w, src_deci_h; uint32_t max_upscale, max_downscale, min_src_size, max_linewidth; bool q16_data = true; + struct sde_phy_plane *pp; + uint32_t num_of_phy_planes = 0; if (!plane || !state) { - SDE_ERROR("invalid arg(s), plane %d state %d\n", - plane != 0, state != 0); + SDE_ERROR("invalid arg(s), plane %d state %d.\n", + plane != NULL, state != NULL); ret = -EINVAL; goto exit; } @@ -1404,11 +1564,8 @@ static int sde_plane_atomic_check(struct drm_plane *plane, psde = to_sde_plane(plane); pstate = to_sde_plane_state(state); - if (!psde->pipe_sblk) { - SDE_ERROR_PLANE(psde, "invalid catalog\n"); - ret = -EINVAL; - goto exit; - } + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) + num_of_phy_planes++; deci_w = sde_plane_get_property(pstate, PLANE_PROP_H_DECIMATE); deci_h = sde_plane_get_property(pstate, PLANE_PROP_V_DECIMATE); @@ -1422,10 +1579,6 @@ static int sde_plane_atomic_check(struct drm_plane *plane, src_deci_w = DECIMATED_DIMENSION(src.w, deci_w); src_deci_h = DECIMATED_DIMENSION(src.h, deci_h); - max_upscale = psde->pipe_sblk->maxupscale; - max_downscale = psde->pipe_sblk->maxdwnscale; - max_linewidth = psde->pipe_sblk->maxlinewidth; - SDE_DEBUG_PLANE(psde, "check %d -> %d\n", sde_plane_enabled(plane->state), sde_plane_enabled(state)); @@ -1436,73 +1589,87 @@ static int sde_plane_atomic_check(struct drm_plane *plane, min_src_size = SDE_FORMAT_IS_YUV(fmt) ? 2 : 1; - if (SDE_FORMAT_IS_YUV(fmt) && - (!(psde->features & SDE_SSPP_SCALER) || - !(psde->features & (BIT(SDE_SSPP_CSC) - | BIT(SDE_SSPP_CSC_10BIT))))) { - SDE_ERROR_PLANE(psde, - "plane doesn't have scaler/csc for yuv\n"); - ret = -EINVAL; - - /* check src bounds */ - } else if (state->fb->width > MAX_IMG_WIDTH || - state->fb->height > MAX_IMG_HEIGHT || - src.w < min_src_size || src.h < min_src_size || - CHECK_LAYER_BOUNDS(src.x, src.w, state->fb->width) || - CHECK_LAYER_BOUNDS(src.y, src.h, state->fb->height)) { - SDE_ERROR_PLANE(psde, "invalid source %u, %u, %ux%u\n", - src.x, src.y, src.w, src.h); - ret = -E2BIG; - - /* valid yuv image */ - } else if (SDE_FORMAT_IS_YUV(fmt) && ((src.x & 0x1) || (src.y & 0x1) || - (src.w & 0x1) || (src.h & 0x1))) { - SDE_ERROR_PLANE(psde, "invalid yuv source %u, %u, %ux%u\n", - src.x, src.y, src.w, src.h); - ret = -EINVAL; + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (!pp->pipe_sblk) { + SDE_ERROR("invalid plane catalog\n"); + ret = -EINVAL; + goto exit; + } - /* min dst support */ - } else if (dst.w < 0x1 || dst.h < 0x1) { - SDE_ERROR_PLANE(psde, "invalid dest rect %u, %u, %ux%u\n", - dst.x, dst.y, dst.w, dst.h); - ret = -EINVAL; + max_upscale = pp->pipe_sblk->maxupscale; + max_downscale = pp->pipe_sblk->maxdwnscale; + max_linewidth = pp->pipe_sblk->maxlinewidth; - /* decimation validation */ - } else if (deci_w || deci_h) { - if ((deci_w > psde->pipe_sblk->maxhdeciexp) || - (deci_h > psde->pipe_sblk->maxvdeciexp)) { + if (SDE_FORMAT_IS_YUV(fmt) && + (!(pp->features & SDE_SSPP_SCALER) || + !(pp->features & (BIT(SDE_SSPP_CSC) + | BIT(SDE_SSPP_CSC_10BIT))))) { SDE_ERROR_PLANE(psde, - "too much decimation requested\n"); + "plane doesn't have scaler/csc for yuv\n"); ret = -EINVAL; - } else if (fmt->fetch_mode != SDE_FETCH_LINEAR) { - SDE_ERROR_PLANE(psde, - "decimation requires linear fetch\n"); + + /* check src bounds */ + } else if (state->fb->width > MAX_IMG_WIDTH || + state->fb->height > MAX_IMG_HEIGHT || + src.w < min_src_size || src.h < min_src_size || + CHECK_LAYER_BOUNDS(src.x, src.w, state->fb->width) || + CHECK_LAYER_BOUNDS(src.y, src.h, state->fb->height)) { + SDE_ERROR_PLANE(psde, "invalid source %u, %u, %ux%u\n", + src.x, src.y, src.w, src.h); + ret = -E2BIG; + + /* valid yuv image */ + } else if (SDE_FORMAT_IS_YUV(fmt) && ((src.x & 0x1) + || (src.y & 0x1) || (src.w & 0x1) + || (src.h & 0x1))) { + SDE_ERROR_PLANE(psde, "invalid yuv source %u, %u,\"\ + %ux%u\n", src.x, src.y, src.w, src.h); ret = -EINVAL; - } - } else if (!(psde->features & SDE_SSPP_SCALER) && - ((src.w != dst.w) || (src.h != dst.h))) { - SDE_ERROR_PLANE(psde, - "pipe doesn't support scaling %ux%u->%ux%u\n", - src.w, src.h, dst.w, dst.h); - ret = -EINVAL; + /* min dst support */ + } else if (dst.w < 0x1 || dst.h < 0x1) { + SDE_ERROR_PLANE(psde, "invalid dest rect %u, %u,\"\ + %ux%u\n", dst.x, dst.y, dst.w, dst.h); + ret = -EINVAL; - /* check decimated source width */ - } else if (src_deci_w > max_linewidth) { - SDE_ERROR_PLANE(psde, - "invalid src w:%u, deci w:%u, line w:%u\n", - src.w, src_deci_w, max_linewidth); - ret = -E2BIG; + /* decimation validation */ + } else if (deci_w || deci_h) { + if ((deci_w > pp->pipe_sblk->maxhdeciexp) || + (deci_h > pp->pipe_sblk->maxvdeciexp)) { + SDE_ERROR_PLANE(psde, + "too much decimation requested\n"); + ret = -EINVAL; + } else if (fmt->fetch_mode != SDE_FETCH_LINEAR) { + SDE_ERROR_PLANE(psde, + "decimation requires linear fetch\n"); + ret = -EINVAL; + } - /* check max scaler capability */ - } else if (((src_deci_w * max_upscale) < dst.w) || - ((src_deci_h * max_upscale) < dst.h) || - ((dst.w * max_downscale) < src_deci_w) || - ((dst.h * max_downscale) < src_deci_h)) { - SDE_ERROR_PLANE(psde, - "too much scaling requested %ux%u->%ux%u\n", - src_deci_w, src_deci_h, dst.w, dst.h); - ret = -E2BIG; + } else if (!(pp->features & SDE_SSPP_SCALER) && + ((src.w != dst.w) || (src.h != dst.h))) { + SDE_ERROR_PLANE(psde, + "pipe doesn't support scaling %ux%u->%ux%u\n", + src.w, src.h, dst.w, dst.h); + ret = -EINVAL; + + /* check decimated source width */ + } else if (src_deci_w > max_linewidth * num_of_phy_planes) { + SDE_ERROR_PLANE(psde, + "invalid src w:%u, deci w:%u, line w:%u, num_phy_planes:%u\n", + src.w, src_deci_w, max_linewidth, + num_of_phy_planes); + ret = -E2BIG; + + /* check max scaler capability */ + } else if (((src_deci_w * max_upscale) < dst.w) || + ((src_deci_h * max_upscale) < dst.h) || + ((dst.w * max_downscale) < src_deci_w) || + ((dst.h * max_downscale) < src_deci_h)) { + SDE_ERROR_PLANE(psde, + "too much scaling requested %ux%u->%ux%u\n", + src_deci_w, src_deci_h, dst.w, dst.h); + ret = -E2BIG; + } } modeset_update: @@ -1519,6 +1686,7 @@ exit: void sde_plane_flush(struct drm_plane *plane) { struct sde_plane *psde; + struct sde_phy_plane *pp; if (!plane) { SDE_ERROR("invalid plane\n"); @@ -1531,14 +1699,17 @@ void sde_plane_flush(struct drm_plane *plane) * These updates have to be done immediately before the plane flush * timing, and may not be moved to the atomic_update/mode_set functions. */ - if (psde->is_error) + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (psde->is_error) /* force white frame with 0% alpha pipe output on error */ - _sde_plane_color_fill(psde, 0xFFFFFF, 0x0); - else if (psde->color_fill & SDE_PLANE_COLOR_FILL_FLAG) - /* force 100% alpha */ - _sde_plane_color_fill(psde, psde->color_fill, 0xFF); - else if (psde->pipe_hw && psde->csc_ptr && psde->pipe_hw->ops.setup_csc) - psde->pipe_hw->ops.setup_csc(psde->pipe_hw, psde->csc_ptr); + _sde_plane_color_fill(pp, 0xFFFFFF, 0x0); + else if (pp->color_fill & SDE_PLANE_COLOR_FILL_FLAG) + /* force 100% alpha */ + _sde_plane_color_fill(pp, pp->color_fill, 0xFF); + else if (pp->pipe_hw && pp->csc_ptr && + pp->pipe_hw->ops.setup_csc) + pp->pipe_hw->ops.setup_csc(pp->pipe_hw, pp->csc_ptr); + } /* flag h/w flush complete */ if (plane->state) @@ -1592,25 +1763,60 @@ static void _sde_plane_install_properties(struct drm_plane *plane, static const struct drm_prop_enum_list e_src_config[] = { {SDE_DRM_DEINTERLACE, "deinterlace"} }; + static const struct drm_prop_enum_list e_fb_translation_mode[] = { + {SDE_DRM_FB_NON_SEC, "non_sec"}, + {SDE_DRM_FB_SEC, "sec"}, + {SDE_DRM_FB_NON_SEC_DIR_TRANS, "non_sec_direct_translation"}, + {SDE_DRM_FB_SEC_DIR_TRANS, "sec_direct_translation"}, + }; const struct sde_format_extended *format_list; struct sde_kms_info *info; struct sde_plane *psde = to_sde_plane(plane); int zpos_max = 255; int zpos_def = 0; char feature_name[256]; + struct sde_phy_plane *pp; + uint32_t features = 0xFFFFFFFF, nformats = 64; + u32 maxlinewidth = -1, maxupscale = -1, maxdwnscale = -1; + u32 maxhdeciexp = -1, maxvdeciexp = -1; if (!plane || !psde) { SDE_ERROR("invalid plane\n"); return; - } else if (!psde->pipe_hw || !psde->pipe_sblk) { - SDE_ERROR("invalid plane, pipe_hw %d pipe_sblk %d\n", - psde->pipe_hw != 0, psde->pipe_sblk != 0); - return; - } else if (!catalog) { + } + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (!pp->pipe_hw || !pp->pipe_sblk) { + SDE_ERROR("invalid phy_plane, pipe_hw %d\"\ + pipe_sblk %d\n", pp->pipe_hw != NULL, + pp->pipe_sblk != NULL); + return; + } + } + if (!catalog) { SDE_ERROR("invalid catalog\n"); return; } + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + /* Get common features for all pipes */ + features &= pp->features; + if (nformats > pp->nformats) { + nformats = pp->nformats; + format_list = pp->pipe_sblk->format_list; + } + if (maxlinewidth < pp->pipe_sblk->maxlinewidth) + maxlinewidth = pp->pipe_sblk->maxlinewidth; + if (maxupscale < pp->pipe_sblk->maxupscale) + maxupscale = pp->pipe_sblk->maxupscale; + if (maxdwnscale < pp->pipe_sblk->maxdwnscale) + maxdwnscale = pp->pipe_sblk->maxdwnscale; + if (maxhdeciexp < pp->pipe_sblk->maxhdeciexp) + maxhdeciexp = pp->pipe_sblk->maxhdeciexp; + if (maxvdeciexp < pp->pipe_sblk->maxvdeciexp) + maxvdeciexp = pp->pipe_sblk->maxvdeciexp; + break; + } + if (sde_is_custom_client()) { if (catalog->mixer_count && catalog->mixer && catalog->mixer[0].sblk->maxblendstages) { @@ -1633,19 +1839,24 @@ static void _sde_plane_install_properties(struct drm_plane *plane, msm_property_install_range(&psde->property_info, "input_fence", 0x0, 0, INR_OPEN_MAX, 0, PLANE_PROP_INPUT_FENCE); - if (psde->pipe_sblk->maxhdeciexp) { - msm_property_install_range(&psde->property_info, "h_decimate", - 0x0, 0, psde->pipe_sblk->maxhdeciexp, 0, - PLANE_PROP_H_DECIMATE); - } + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (pp->pipe_sblk->maxhdeciexp) { + msm_property_install_range(&psde->property_info, + "h_decimate", 0x0, 0, + pp->pipe_sblk->maxhdeciexp, 0, + PLANE_PROP_H_DECIMATE); + } - if (psde->pipe_sblk->maxvdeciexp) { - msm_property_install_range(&psde->property_info, "v_decimate", - 0x0, 0, psde->pipe_sblk->maxvdeciexp, 0, + if (pp->pipe_sblk->maxvdeciexp) { + msm_property_install_range(&psde->property_info, + "v_decimate", 0x0, 0, + pp->pipe_sblk->maxvdeciexp, 0, PLANE_PROP_V_DECIMATE); + } + break; } - if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) { + if (features & BIT(SDE_SSPP_SCALER_QSEED3)) { msm_property_install_volatile_range(&psde->property_info, "scaler_v2", 0x0, 0, ~0, 0, PLANE_PROP_SCALER_V2); msm_property_install_blob(&psde->property_info, "lut_ed", 0, @@ -1654,38 +1865,38 @@ static void _sde_plane_install_properties(struct drm_plane *plane, PLANE_PROP_SCALER_LUT_CIR); msm_property_install_blob(&psde->property_info, "lut_sep", 0, PLANE_PROP_SCALER_LUT_SEP); - } else if (psde->features & SDE_SSPP_SCALER) { + } else if (features & SDE_SSPP_SCALER) { msm_property_install_volatile_range(&psde->property_info, "scaler_v1", 0x0, 0, ~0, 0, PLANE_PROP_SCALER_V1); } - if (psde->features & BIT(SDE_SSPP_CSC)) { + if (features & BIT(SDE_SSPP_CSC)) { msm_property_install_volatile_range(&psde->property_info, "csc_v1", 0x0, 0, ~0, 0, PLANE_PROP_CSC_V1); } - if (psde->features & BIT(SDE_SSPP_HSIC)) { + if (features & BIT(SDE_SSPP_HSIC)) { snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_HUE_V", - psde->pipe_sblk->hsic_blk.version >> 16); + pp->pipe_sblk->hsic_blk.version >> 16); msm_property_install_range(&psde->property_info, feature_name, 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_HUE_ADJUST); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_SATURATION_V", - psde->pipe_sblk->hsic_blk.version >> 16); + pp->pipe_sblk->hsic_blk.version >> 16); msm_property_install_range(&psde->property_info, feature_name, 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_SATURATION_ADJUST); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_VALUE_V", - psde->pipe_sblk->hsic_blk.version >> 16); + pp->pipe_sblk->hsic_blk.version >> 16); msm_property_install_range(&psde->property_info, feature_name, 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_VALUE_ADJUST); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_CONTRAST_V", - psde->pipe_sblk->hsic_blk.version >> 16); + pp->pipe_sblk->hsic_blk.version >> 16); msm_property_install_range(&psde->property_info, feature_name, 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_CONTRAST_ADJUST); @@ -1701,9 +1912,13 @@ static void _sde_plane_install_properties(struct drm_plane *plane, msm_property_install_enum(&psde->property_info, "src_config", 0x0, 1, e_src_config, ARRAY_SIZE(e_src_config), PLANE_PROP_SRC_CONFIG); - if (psde->pipe_hw->ops.setup_solidfill) - msm_property_install_range(&psde->property_info, "color_fill", - 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_COLOR_FILL); + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + if (pp->pipe_hw->ops.setup_solidfill) + msm_property_install_range(&psde->property_info, + "color_fill", 0, 0, 0xFFFFFFFF, 0, + PLANE_PROP_COLOR_FILL); + break; + } info = kzalloc(sizeof(struct sde_kms_info), GFP_KERNEL); if (!info) { @@ -1715,7 +1930,6 @@ static void _sde_plane_install_properties(struct drm_plane *plane, DRM_MODE_PROP_IMMUTABLE, PLANE_PROP_INFO); sde_kms_info_reset(info); - format_list = psde->pipe_sblk->format_list; if (format_list) { sde_kms_info_start(info, "pixel_formats"); while (format_list->fourcc_format) { @@ -1727,51 +1941,55 @@ static void _sde_plane_install_properties(struct drm_plane *plane, sde_kms_info_stop(info); } - sde_kms_info_add_keyint(info, "max_linewidth", - psde->pipe_sblk->maxlinewidth); - sde_kms_info_add_keyint(info, "max_upscale", - psde->pipe_sblk->maxupscale); - sde_kms_info_add_keyint(info, "max_downscale", - psde->pipe_sblk->maxdwnscale); - sde_kms_info_add_keyint(info, "max_horizontal_deci", - psde->pipe_sblk->maxhdeciexp); - sde_kms_info_add_keyint(info, "max_vertical_deci", - psde->pipe_sblk->maxvdeciexp); + sde_kms_info_add_keyint(info, "max_linewidth", maxlinewidth); + sde_kms_info_add_keyint(info, "max_upscale", maxupscale); + sde_kms_info_add_keyint(info, "max_downscale", maxdwnscale); + sde_kms_info_add_keyint(info, "max_horizontal_deci", maxhdeciexp); + sde_kms_info_add_keyint(info, "max_vertical_deci", maxvdeciexp); msm_property_set_blob(&psde->property_info, &psde->blob_info, info->data, info->len, PLANE_PROP_INFO); kfree(info); - if (psde->features & BIT(SDE_SSPP_MEMCOLOR)) { + if (features & BIT(SDE_SSPP_MEMCOLOR)) { snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_SKIN_COLOR_V", - psde->pipe_sblk->memcolor_blk.version >> 16); + pp->pipe_sblk->memcolor_blk.version >> 16); msm_property_install_blob(&psde->property_info, feature_name, 0, PLANE_PROP_SKIN_COLOR); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_SKY_COLOR_V", - psde->pipe_sblk->memcolor_blk.version >> 16); + pp->pipe_sblk->memcolor_blk.version >> 16); msm_property_install_blob(&psde->property_info, feature_name, 0, PLANE_PROP_SKY_COLOR); snprintf(feature_name, sizeof(feature_name), "%s%d", "SDE_SSPP_FOLIAGE_COLOR_V", - psde->pipe_sblk->memcolor_blk.version >> 16); + pp->pipe_sblk->memcolor_blk.version >> 16); msm_property_install_blob(&psde->property_info, feature_name, 0, PLANE_PROP_FOLIAGE_COLOR); } + + msm_property_install_enum(&psde->property_info, "fb_translation_mode", + 0x0, + 0, e_fb_translation_mode, + ARRAY_SIZE(e_fb_translation_mode), + PLANE_PROP_FB_TRANSLATION_MODE); } -static inline void _sde_plane_set_csc_v1(struct sde_plane *psde, void *usr_ptr) +static inline void _sde_plane_set_csc_v1(struct sde_phy_plane *pp, + void *usr_ptr) { struct sde_drm_csc_v1 csc_v1; + struct sde_plane *psde; int i; - if (!psde) { - SDE_ERROR("invalid plane\n"); + if (!pp) { + SDE_ERROR("invalid phy_plane\n"); return; } + psde = pp->sde_plane; - psde->csc_usr_ptr = NULL; + pp->csc_usr_ptr = NULL; if (!usr_ptr) { SDE_DEBUG_PLANE(psde, "csc data removed\n"); return; @@ -1784,30 +2002,33 @@ static inline void _sde_plane_set_csc_v1(struct sde_plane *psde, void *usr_ptr) /* populate from user space */ for (i = 0; i < SDE_CSC_MATRIX_COEFF_SIZE; ++i) - psde->csc_cfg.csc_mv[i] = csc_v1.ctm_coeff[i] >> 16; + pp->csc_cfg.csc_mv[i] = csc_v1.ctm_coeff[i] >> 16; for (i = 0; i < SDE_CSC_BIAS_SIZE; ++i) { - psde->csc_cfg.csc_pre_bv[i] = csc_v1.pre_bias[i]; - psde->csc_cfg.csc_post_bv[i] = csc_v1.post_bias[i]; + pp->csc_cfg.csc_pre_bv[i] = csc_v1.pre_bias[i]; + pp->csc_cfg.csc_post_bv[i] = csc_v1.post_bias[i]; } for (i = 0; i < SDE_CSC_CLAMP_SIZE; ++i) { - psde->csc_cfg.csc_pre_lv[i] = csc_v1.pre_clamp[i]; - psde->csc_cfg.csc_post_lv[i] = csc_v1.post_clamp[i]; + pp->csc_cfg.csc_pre_lv[i] = csc_v1.pre_clamp[i]; + pp->csc_cfg.csc_post_lv[i] = csc_v1.post_clamp[i]; } - psde->csc_usr_ptr = &psde->csc_cfg; + pp->csc_usr_ptr = &pp->csc_cfg; } -static inline void _sde_plane_set_scaler_v1(struct sde_plane *psde, void *usr) +static inline void _sde_plane_set_scaler_v1(struct sde_phy_plane *pp, + void *usr) { struct sde_drm_scaler_v1 scale_v1; struct sde_hw_pixel_ext *pe; + struct sde_plane *psde; int i; - if (!psde) { - SDE_ERROR("invalid plane\n"); + if (!pp) { + SDE_ERROR("invalid phy_plane\n"); return; } + psde = pp->sde_plane; - psde->pixel_ext_usr = false; + pp->pixel_ext_usr = false; if (!usr) { SDE_DEBUG_PLANE(psde, "scale data removed\n"); return; @@ -1819,7 +2040,7 @@ static inline void _sde_plane_set_scaler_v1(struct sde_plane *psde, void *usr) } /* populate from user space */ - pe = &(psde->pixel_ext); + pe = &(pp->pixel_ext); memset(pe, 0, sizeof(struct sde_hw_pixel_ext)); for (i = 0; i < SDE_MAX_PLANES; i++) { pe->init_phase_x[i] = scale_v1.init_phase_x[i]; @@ -1844,26 +2065,28 @@ static inline void _sde_plane_set_scaler_v1(struct sde_plane *psde, void *usr) pe->roi_h[i] = scale_v1.pe.num_ext_pxls_tb[i]; } - psde->pixel_ext_usr = true; + pp->pixel_ext_usr = true; SDE_DEBUG_PLANE(psde, "user property data copied\n"); } -static inline void _sde_plane_set_scaler_v2(struct sde_plane *psde, +static inline void _sde_plane_set_scaler_v2(struct sde_phy_plane *pp, struct sde_plane_state *pstate, void *usr) { struct sde_drm_scaler_v2 scale_v2; struct sde_hw_pixel_ext *pe; int i; struct sde_hw_scaler3_cfg *cfg; + struct sde_plane *psde; - if (!psde) { - SDE_ERROR("invalid plane\n"); + if (!pp) { + SDE_ERROR("invalid phy_plane\n"); return; } + psde = pp->sde_plane; - cfg = psde->scaler3_cfg; - psde->pixel_ext_usr = false; + cfg = pp->scaler3_cfg; + pp->pixel_ext_usr = false; if (!usr) { SDE_DEBUG_PLANE(psde, "scale data removed\n"); return; @@ -1875,7 +2098,7 @@ static inline void _sde_plane_set_scaler_v2(struct sde_plane *psde, } /* populate from user space */ - pe = &(psde->pixel_ext); + pe = &(pp->pixel_ext); memset(pe, 0, sizeof(struct sde_hw_pixel_ext)); cfg->enable = scale_v2.enable; cfg->dir_en = scale_v2.dir_en; @@ -1933,7 +2156,7 @@ static inline void _sde_plane_set_scaler_v2(struct sde_plane *psde, pe->btm_rpt[i] = scale_v2.pe.btm_rpt[i]; pe->roi_h[i] = scale_v2.pe.num_ext_pxls_tb[i]; } - psde->pixel_ext_usr = true; + pp->pixel_ext_usr = true; SDE_DEBUG_PLANE(psde, "user property data copied\n"); } @@ -1945,6 +2168,7 @@ static int sde_plane_atomic_set_property(struct drm_plane *plane, struct sde_plane *psde = plane ? to_sde_plane(plane) : NULL; struct sde_plane_state *pstate; int idx, ret = -EINVAL; + struct sde_phy_plane *pp; SDE_DEBUG_PLANE(psde, "\n"); @@ -1965,14 +2189,24 @@ static int sde_plane_atomic_set_property(struct drm_plane *plane, _sde_plane_set_input_fence(psde, pstate, val); break; case PLANE_PROP_CSC_V1: - _sde_plane_set_csc_v1(psde, (void *)val); + list_for_each_entry(pp, &psde->phy_plane_head, + phy_plane_list) { + _sde_plane_set_csc_v1(pp, (void *)val); + } break; case PLANE_PROP_SCALER_V1: - _sde_plane_set_scaler_v1(psde, (void *)val); + list_for_each_entry(pp, &psde->phy_plane_head, + phy_plane_list) { + _sde_plane_set_scaler_v1(pp, + (void *)val); + } break; case PLANE_PROP_SCALER_V2: - _sde_plane_set_scaler_v2(psde, pstate, - (void *)val); + list_for_each_entry(pp, &psde->phy_plane_head, + phy_plane_list) { + _sde_plane_set_scaler_v2(pp, pstate, + (void *)val); + } break; default: /* nothing to do */ @@ -2019,12 +2253,15 @@ static int sde_plane_atomic_get_property(struct drm_plane *plane, static void sde_plane_destroy(struct drm_plane *plane) { struct sde_plane *psde = plane ? to_sde_plane(plane) : NULL; + struct sde_phy_plane *pp, *n; SDE_DEBUG_PLANE(psde, "\n"); if (psde) { - _sde_plane_set_qos_ctrl(plane, false, SDE_PLANE_QOS_PANIC_CTRL); - + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + _sde_plane_set_qos_ctrl(pp, + false, SDE_PLANE_QOS_PANIC_CTRL); + } debugfs_remove_recursive(psde->debugfs_root); if (psde->blob_info) @@ -2037,8 +2274,13 @@ static void sde_plane_destroy(struct drm_plane *plane) /* this will destroy the states as well */ drm_plane_cleanup(plane); - if (psde->pipe_hw) - sde_hw_sspp_destroy(psde->pipe_hw); + list_for_each_entry_safe(pp, n, + &psde->phy_plane_head, phy_plane_list) { + if (pp->pipe_hw) + sde_hw_sspp_destroy(pp->pipe_hw); + list_del(&pp->phy_plane_list); + kfree(pp); + } kfree(psde); } @@ -2174,9 +2416,22 @@ static const struct drm_plane_helper_funcs sde_plane_helper_funcs = { .atomic_update = sde_plane_atomic_update, }; -enum sde_sspp sde_plane_pipe(struct drm_plane *plane) +enum sde_sspp sde_plane_pipe(struct drm_plane *plane, uint32_t index) { - return plane ? to_sde_plane(plane)->pipe : SSPP_NONE; + struct sde_plane *sde_plane = to_sde_plane(plane); + struct sde_phy_plane *pp; + int i = 0; + enum sde_sspp default_sspp = SSPP_NONE; + + list_for_each_entry(pp, &sde_plane->phy_plane_head, phy_plane_list) { + if (i == 0) + default_sspp = pp->pipe; + if (i == index) + return pp->pipe; + i++; + } + + return default_sspp; } static ssize_t _sde_plane_danger_read(struct file *file, @@ -2208,10 +2463,16 @@ static ssize_t _sde_plane_danger_read(struct file *file, static void _sde_plane_set_danger_state(struct sde_kms *kms, bool enable) { struct drm_plane *plane; + struct sde_plane *psde; + struct sde_phy_plane *pp; drm_for_each_plane(plane, kms->dev) { if (plane->fb && plane->state) { - sde_plane_danger_signal_ctrl(plane, enable); + psde = to_sde_plane(plane); + list_for_each_entry(pp, &psde->phy_plane_head, + phy_plane_list) { + sde_plane_danger_signal_ctrl(pp, enable); + } SDE_DEBUG("plane:%d img:%dx%d ", plane->base.id, plane->fb->width, plane->fb->height); @@ -2229,7 +2490,7 @@ static void _sde_plane_set_danger_state(struct sde_kms *kms, bool enable) } static ssize_t _sde_plane_danger_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) + const char __user *user_buf, size_t count, loff_t *ppos) { struct sde_kms *kms = file->private_data; struct sde_mdss_cfg *cfg = kms->catalog; @@ -2271,85 +2532,166 @@ static const struct file_operations sde_plane_danger_enable = { .write = _sde_plane_danger_write, }; -static void _sde_plane_init_debugfs(struct sde_plane *psde, struct sde_kms *kms) +static void _sde_plane_init_debugfs(struct sde_plane *psde, + struct sde_kms *kms) { const struct sde_sspp_sub_blks *sblk = 0; const struct sde_sspp_cfg *cfg = 0; + struct sde_phy_plane *pp; - if (psde && psde->pipe_hw) - cfg = psde->pipe_hw->cap; - if (cfg) + if (!psde || !kms) { + SDE_ERROR("invalid arg(s), psde %d kms %d\n", + psde != NULL, kms != NULL); + return; + } + + /* create overall sub-directory for the pipe */ + psde->debugfs_root = debugfs_create_dir(psde->pipe_name, + sde_debugfs_get_root(kms)); + if (!psde->debugfs_root) + return; + + list_for_each_entry(pp, &psde->phy_plane_head, phy_plane_list) { + debugfs_create_u32("pipe", S_IRUGO | S_IWUSR, + psde->debugfs_root, &pp->pipe); + + if (!pp->pipe_hw || !pp->pipe_hw->cap || + !pp->pipe_hw->cap->sblk) + continue; + cfg = pp->pipe_hw->cap; sblk = cfg->sblk; - if (kms && sblk) { - /* create overall sub-directory for the pipe */ - psde->debugfs_root = - debugfs_create_dir(psde->pipe_name, - sde_debugfs_get_root(kms)); - if (psde->debugfs_root) { - /* don't error check these */ - debugfs_create_x32("features", S_IRUGO | S_IWUSR, - psde->debugfs_root, &psde->features); - - /* add register dump support */ - sde_debugfs_setup_regset32(&psde->debugfs_src, - sblk->src_blk.base + cfg->base, - sblk->src_blk.len, - kms); - sde_debugfs_create_regset32("src_blk", S_IRUGO, - psde->debugfs_root, &psde->debugfs_src); - - sde_debugfs_setup_regset32(&psde->debugfs_scaler, - sblk->scaler_blk.base + cfg->base, - sblk->scaler_blk.len, - kms); - sde_debugfs_create_regset32("scaler_blk", S_IRUGO, - psde->debugfs_root, - &psde->debugfs_scaler); - - sde_debugfs_setup_regset32(&psde->debugfs_csc, - sblk->csc_blk.base + cfg->base, - sblk->csc_blk.len, - kms); - sde_debugfs_create_regset32("csc_blk", S_IRUGO, - psde->debugfs_root, &psde->debugfs_csc); - - debugfs_create_u32("xin_id", - S_IRUGO, - psde->debugfs_root, - (u32 *) &cfg->xin_id); - debugfs_create_u32("clk_ctrl", - S_IRUGO, - psde->debugfs_root, - (u32 *) &cfg->clk_ctrl); - debugfs_create_x32("creq_vblank", - S_IRUGO | S_IWUSR, - psde->debugfs_root, - (u32 *) &sblk->creq_vblank); - debugfs_create_x32("danger_vblank", - S_IRUGO | S_IWUSR, - psde->debugfs_root, - (u32 *) &sblk->danger_vblank); - - debugfs_create_file("disable_danger", - S_IRUGO | S_IWUSR, - psde->debugfs_root, - kms, &sde_plane_danger_enable); + /* don't error check these */ + debugfs_create_x32("features", S_IRUGO | S_IWUSR, + psde->debugfs_root, &pp->features); + + /* add register dump support */ + sde_debugfs_setup_regset32(&psde->debugfs_src, + sblk->src_blk.base + cfg->base, + sblk->src_blk.len, + kms); + sde_debugfs_create_regset32("src_blk", S_IRUGO, + psde->debugfs_root, &psde->debugfs_src); + + sde_debugfs_setup_regset32(&psde->debugfs_scaler, + sblk->scaler_blk.base + cfg->base, + sblk->scaler_blk.len, + kms); + sde_debugfs_create_regset32("scaler_blk", S_IRUGO, + psde->debugfs_root, + &psde->debugfs_scaler); + + sde_debugfs_setup_regset32(&psde->debugfs_csc, + sblk->csc_blk.base + cfg->base, + sblk->csc_blk.len, + kms); + sde_debugfs_create_regset32("csc_blk", S_IRUGO, + psde->debugfs_root, &psde->debugfs_csc); + + debugfs_create_u32("xin_id", + S_IRUGO, + psde->debugfs_root, + (u32 *) &cfg->xin_id); + debugfs_create_u32("clk_ctrl", + S_IRUGO, + psde->debugfs_root, + (u32 *) &cfg->clk_ctrl); + debugfs_create_x32("creq_vblank", + S_IRUGO | S_IWUSR, + psde->debugfs_root, + (u32 *) &sblk->creq_vblank); + debugfs_create_x32("danger_vblank", + S_IRUGO | S_IWUSR, + psde->debugfs_root, + (u32 *) &sblk->danger_vblank); + + debugfs_create_file("disable_danger", + S_IRUGO | S_IWUSR, + psde->debugfs_root, + kms, &sde_plane_danger_enable); + + break; + } +} + +static int _sde_init_phy_plane(struct sde_kms *sde_kms, + struct sde_plane *psde, uint32_t pipe, uint32_t index, + struct sde_phy_plane *pp) +{ + int rc = 0; + + pp->pipe_hw = sde_rm_get_hw_by_id(&sde_kms->rm, + SDE_HW_BLK_SSPP, pipe); + if (!pp->pipe_hw) { + SDE_ERROR("Not found resource for id=%d\n", pipe); + rc = -EINVAL; + goto end; + } else if (!pp->pipe_hw->cap || !pp->pipe_hw->cap->sblk) { + SDE_ERROR("[%u]SSPP returned invalid cfg\n", pipe); + rc = -EINVAL; + goto end; + } + + /* cache features mask for later */ + pp->features = pp->pipe_hw->cap->features; + pp->pipe_sblk = pp->pipe_hw->cap->sblk; + if (!pp->pipe_sblk) { + SDE_ERROR("invalid sblk on pipe %d\n", pipe); + rc = -EINVAL; + goto end; + } + + if (pp->features & BIT(SDE_SSPP_SCALER_QSEED3)) { + pp->scaler3_cfg = kzalloc(sizeof(struct sde_hw_scaler3_cfg), + GFP_KERNEL); + if (!pp->scaler3_cfg) { + SDE_ERROR("[%u]failed to allocate scale struct\n", + pipe); + rc = -ENOMEM; + goto end; } } + + /* add plane to DRM framework */ + pp->nformats = sde_populate_formats( + pp->pipe_sblk->format_list, + pp->formats, + NULL, + ARRAY_SIZE(pp->formats)); + + if (!pp->nformats) { + SDE_ERROR("[%u]no valid formats for plane\n", pipe); + if (pp->scaler3_cfg) + kzfree(pp->scaler3_cfg); + + rc = -EINVAL; + goto end; + } + + pp->sde_plane = psde; + pp->pipe = pipe; + pp->index = index; + +end: + return rc; } /* initialize plane */ struct drm_plane *sde_plane_init(struct drm_device *dev, uint32_t pipe, bool primary_plane, - unsigned long possible_crtcs) + unsigned long possible_crtcs, bool vp_enabled) { struct drm_plane *plane = NULL; struct sde_plane *psde; + struct sde_phy_plane *pp, *n; struct msm_drm_private *priv; struct sde_kms *kms; enum drm_plane_type type; int ret = -EINVAL; + struct sde_vp_cfg *vp; + struct sde_vp_sub_blks *vp_sub; + uint32_t features = 0xFFFFFFFF, nformats = 64, formats[64]; + uint32_t index = 0; if (!dev) { SDE_ERROR("[%u]device is NULL\n", pipe); @@ -2383,60 +2725,76 @@ struct drm_plane *sde_plane_init(struct drm_device *dev, /* cache local stuff for later */ plane = &psde->base; - psde->pipe = pipe; - psde->aspace = kms->aspace[MSM_SMMU_DOMAIN_UNSECURE]; - /* initialize underlying h/w driver */ - psde->pipe_hw = sde_hw_sspp_init(pipe, kms->mmio, kms->catalog); - if (IS_ERR(psde->pipe_hw)) { - SDE_ERROR("[%u]SSPP init failed\n", pipe); - ret = PTR_ERR(psde->pipe_hw); - goto clean_plane; - } else if (!psde->pipe_hw->cap || !psde->pipe_hw->cap->sblk) { - SDE_ERROR("[%u]SSPP init returned invalid cfg\n", pipe); - goto clean_sspp; - } + INIT_LIST_HEAD(&psde->phy_plane_head); - /* cache features mask for later */ - psde->features = psde->pipe_hw->cap->features; - psde->pipe_sblk = psde->pipe_hw->cap->sblk; - if (!psde->pipe_sblk) { - SDE_ERROR("[%u]invalid sblk\n", pipe); - goto clean_sspp; - } + /* initialize underlying h/w driver */ + if (vp_enabled) { + vp = &(kms->catalog->vp[pipe]); + list_for_each_entry(vp_sub, &vp->sub_blks, pipeid_list) { + pp = kzalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) { + SDE_ERROR("out of memory\n"); + ret = -ENOMEM; + goto clean_plane; + } - if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) { - psde->scaler3_cfg = kzalloc(sizeof(struct sde_hw_scaler3_cfg), - GFP_KERNEL); - if (!psde->scaler3_cfg) { - SDE_ERROR("[%u]failed to allocate scale struct\n", - pipe); + ret = _sde_init_phy_plane(kms, psde, vp_sub->sspp_id, + index, pp); + if (ret) { + SDE_ERROR("_sde_init_phy_plane error vp=%d\n", + pipe); + kfree(pp); + ret = -EINVAL; + goto clean_plane; + } + /* Get common features for all pipes */ + features &= pp->features; + if (nformats > pp->nformats) { + nformats = pp->nformats; + memcpy(formats, pp->formats, + sizeof(formats)); + } + list_add_tail(&pp->phy_plane_list, + &psde->phy_plane_head); + index++; + psde->num_of_phy_planes++; + } + } else { + pp = kzalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) { + SDE_ERROR("out of memory\n"); ret = -ENOMEM; - goto clean_sspp; + goto clean_plane; } - } - - /* add plane to DRM framework */ - psde->nformats = sde_populate_formats(psde->pipe_sblk->format_list, - psde->formats, - 0, - ARRAY_SIZE(psde->formats)); - if (!psde->nformats) { - SDE_ERROR("[%u]no valid formats for plane\n", pipe); - goto clean_sspp; + ret = _sde_init_phy_plane(kms, psde, pipe, index, pp); + if (ret) { + SDE_ERROR("_sde_init_phy_plane error id=%d\n", + pipe); + kfree(pp); + ret = -EINVAL; + goto clean_plane; + } + features = pp->features; + nformats = pp->nformats; + memcpy(formats, pp->formats, + sizeof(uint32_t) * 64); + list_add_tail(&pp->phy_plane_list, + &psde->phy_plane_head); + psde->num_of_phy_planes++; } - if (psde->features & BIT(SDE_SSPP_CURSOR)) + if (features & BIT(SDE_SSPP_CURSOR)) type = DRM_PLANE_TYPE_CURSOR; else if (primary_plane) type = DRM_PLANE_TYPE_PRIMARY; else type = DRM_PLANE_TYPE_OVERLAY; ret = drm_universal_plane_init(dev, plane, possible_crtcs, - &sde_plane_funcs, psde->formats, psde->nformats, type); + &sde_plane_funcs, formats, nformats, type); if (ret) - goto clean_sspp; + goto clean_plane; /* success! finalize initialization */ drm_plane_helper_add(plane, &sde_plane_helper_funcs); @@ -2458,14 +2816,20 @@ struct drm_plane *sde_plane_init(struct drm_device *dev, DRM_INFO("%s created for pipe %u\n", psde->pipe_name, pipe); return plane; -clean_sspp: - if (psde && psde->pipe_hw) - sde_hw_sspp_destroy(psde->pipe_hw); - - if (psde && psde->scaler3_cfg) - kfree(psde->scaler3_cfg); clean_plane: - kfree(psde); + if (psde) { + list_for_each_entry_safe(pp, n, + &psde->phy_plane_head, phy_plane_list) { + if (pp->pipe_hw) + sde_hw_sspp_destroy(pp->pipe_hw); + + kfree(pp->scaler3_cfg); + list_del(&pp->phy_plane_list); + kfree(pp); + } + kfree(psde); + } + exit: return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h index 1514f633c61e..7b91822d4cde 100644 --- a/drivers/gpu/drm/msm/sde/sde_plane.h +++ b/drivers/gpu/drm/msm/sde/sde_plane.h @@ -59,9 +59,10 @@ struct sde_plane_state { /** * sde_plane_pipe - return sspp identifier for the given plane * @plane: Pointer to DRM plane object + * @index: Plane index * Returns: sspp identifier of the given plane */ -enum sde_sspp sde_plane_pipe(struct drm_plane *plane); +enum sde_sspp sde_plane_pipe(struct drm_plane *plane, uint32_t index); /** * sde_plane_flush - final plane operations before commit flush @@ -75,10 +76,11 @@ void sde_plane_flush(struct drm_plane *plane); * @pipe: sde hardware pipe identifier * @primary_plane: true if this pipe is primary plane for crtc * @possible_crtcs: bitmask of crtc that can be attached to the given pipe + * @vp_enabled: Flag indicating if virtual planes enabled */ struct drm_plane *sde_plane_init(struct drm_device *dev, uint32_t pipe, bool primary_plane, - unsigned long possible_crtcs); + unsigned long possible_crtcs, bool vp_enabled); /** * sde_plane_wait_input_fence - wait for input fence object diff --git a/drivers/gpu/drm/msm/sde/sde_rm.c b/drivers/gpu/drm/msm/sde/sde_rm.c index fca0768e2734..fe4b73b4ffea 100644 --- a/drivers/gpu/drm/msm/sde/sde_rm.c +++ b/drivers/gpu/drm/msm/sde/sde_rm.c @@ -23,6 +23,7 @@ #include "sde_hw_wb.h" #include "sde_encoder.h" #include "sde_connector.h" +#include "sde_hw_sspp.h" #define RESERVED_BY_OTHER(h, r) \ ((h)->rsvp && ((h)->rsvp->enc_id != (r)->enc_id)) @@ -197,6 +198,33 @@ bool sde_rm_get_hw(struct sde_rm *rm, struct sde_rm_hw_iter *i) return false; } +void *sde_rm_get_hw_by_id(struct sde_rm *rm, enum sde_hw_blk_type type, int id) +{ + struct list_head *blk_list; + struct sde_rm_hw_blk *blk; + void *hw = NULL; + + if (!rm || type >= SDE_HW_BLK_MAX) { + SDE_ERROR("invalid rm\n"); + return hw; + } + + blk_list = &rm->hw_blks[type]; + + list_for_each_entry(blk, blk_list, list) { + if (blk->id == id) { + hw = blk->hw; + SDE_DEBUG("found type %d %s id %d\n", + type, blk->type_name, blk->id); + return hw; + } + } + + SDE_DEBUG("no match, type %d id=%d\n", type, id); + + return hw; +} + static void _sde_rm_hw_destroy(enum sde_hw_blk_type type, void *hw) { switch (type) { @@ -222,7 +250,8 @@ static void _sde_rm_hw_destroy(enum sde_hw_blk_type type, void *hw) sde_hw_wb_destroy(hw); break; case SDE_HW_BLK_SSPP: - /* SSPPs are not managed by the resource manager */ + sde_hw_sspp_destroy(hw); + break; case SDE_HW_BLK_TOP: /* Top is a singleton, not managed in hw_blks list */ case SDE_HW_BLK_MAX: @@ -310,7 +339,9 @@ static int _sde_rm_hw_blk_create( name = "wb"; break; case SDE_HW_BLK_SSPP: - /* SSPPs are not managed by the resource manager */ + hw = sde_hw_sspp_init(id, (void __iomem *)mmio, cat); + name = "sspp"; + break; case SDE_HW_BLK_TOP: /* Top is a singleton, not managed in hw_blks list */ case SDE_HW_BLK_MAX: @@ -369,6 +400,13 @@ int sde_rm_init(struct sde_rm *rm, goto fail; } + for (i = 0; i < cat->sspp_count; i++) { + rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_SSPP, + cat->sspp[i].id, &cat->sspp[i]); + if (rc) + goto fail; + } + /* Interrogate HW catalog and create tracking items for hw blocks */ for (i = 0; i < cat->mixer_count; i++) { struct sde_lm_cfg *lm = &cat->mixer[i]; diff --git a/drivers/gpu/drm/msm/sde/sde_rm.h b/drivers/gpu/drm/msm/sde/sde_rm.h index 855b12ce8150..1cc22c5fbbf4 100644 --- a/drivers/gpu/drm/msm/sde/sde_rm.h +++ b/drivers/gpu/drm/msm/sde/sde_rm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -185,6 +185,18 @@ void sde_rm_init_hw_iter( bool sde_rm_get_hw(struct sde_rm *rm, struct sde_rm_hw_iter *iter); /** + * sde_rm_get_hw_by_id - retrieve hw object given hw type and hw id + * Meant to do a single pass through the hardware list to iteratively + * retrieve hardware blocks of a given type and id. + * Function returns the hw resource pointer. + * @rm: SDE Resource Manager handle + * @type: hw type + * @id: hw id + * @Return: hw resource pointer on match found, NULL on no match found + */ +void *sde_rm_get_hw_by_id(struct sde_rm *rm, enum sde_hw_blk_type type, int id); + +/** * sde_rm_check_property_topctl - validate property bitmask before it is set * @val: user's proposed topology control bitmask * @Return: 0 on success or error diff --git a/drivers/gpu/drm/msm/sde_hdcp.h b/drivers/gpu/drm/msm/sde_hdcp.h new file mode 100644 index 000000000000..49cca9399cb0 --- /dev/null +++ b/drivers/gpu/drm/msm/sde_hdcp.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef __SDE_HDCP_H__ +#define __SDE_HDCP_H__ + +#include <soc/qcom/scm.h> + +#include <linux/types.h> +#include <linux/bitops.h> +#include <linux/debugfs.h> +#include <linux/of_device.h> +#include <linux/i2c.h> +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_edid.h> +#include "hdmi.h" +#include "sde_kms.h" +#include "sde_hdmi_util.h" + +#ifdef SDE_HDCP_DEBUG_ENABLE +#define SDE_HDCP_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args) +#else +#define SDE_HDCP_DEBUG(fmt, args...) SDE_DEBUG(fmt, ##args) +#endif + +enum sde_hdcp_client_id { + HDCP_CLIENT_HDMI, + HDCP_CLIENT_DP, +}; + +enum sde_hdcp_states { + HDCP_STATE_INACTIVE, + HDCP_STATE_AUTHENTICATING, + HDCP_STATE_AUTHENTICATED, + HDCP_STATE_AUTH_FAIL, + HDCP_STATE_AUTH_ENC_NONE, + HDCP_STATE_AUTH_ENC_1X, + HDCP_STATE_AUTH_ENC_2P2 +}; + +struct sde_hdcp_init_data { + struct dss_io_data *core_io; + struct dss_io_data *qfprom_io; + struct dss_io_data *hdcp_io; + struct mutex *mutex; + struct workqueue_struct *workq; + void *cb_data; + void (*notify_status)(void *cb_data, enum sde_hdcp_states status); + struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl; + u8 sink_rx_status; + u16 *version; + u32 phy_addr; + u32 hdmi_tx_ver; + bool sec_access; + enum sde_hdcp_client_id client_id; +}; + +struct sde_hdcp_ops { + int (*isr)(void *ptr); + int (*cp_irq)(void *ptr); + int (*reauthenticate)(void *input); + int (*authenticate)(void *hdcp_ctrl); + bool (*feature_supported)(void *input); + void (*off)(void *hdcp_ctrl); +}; + +void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data); +void sde_hdcp_1x_deinit(void *input); +struct sde_hdcp_ops *sde_hdcp_1x_start(void *input); +void *sde_hdmi_hdcp2p2_init(struct sde_hdcp_init_data *init_data); +void sde_hdmi_hdcp2p2_deinit(void *input); +const char *sde_hdcp_state_name(enum sde_hdcp_states hdcp_state); +struct sde_hdcp_ops *sde_hdmi_hdcp2p2_start(void *input); +#endif /* __SDE_HDCP_H__ */ diff --git a/drivers/gpu/drm/msm/sde_hdcp_1x.c b/drivers/gpu/drm/msm/sde_hdcp_1x.c new file mode 100644 index 000000000000..3aba9e307732 --- /dev/null +++ b/drivers/gpu/drm/msm/sde_hdcp_1x.c @@ -0,0 +1,1722 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/io.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/stat.h> +#include <linux/iopoll.h> +#include <linux/hdcp_qseecom.h> +#include "sde_hdcp.h" +#include "sde_hdmi_util.h" +#include "video/msm_hdmi_hdcp_mgr.h" + +#define SDE_HDCP_STATE_NAME (sde_hdcp_state_name(hdcp->hdcp_state)) + +/* HDCP Keys state based on HDMI_HDCP_LINK0_STATUS:KEYS_STATE */ +#define HDCP_KEYS_STATE_NO_KEYS 0 +#define HDCP_KEYS_STATE_NOT_CHECKED 1 +#define HDCP_KEYS_STATE_CHECKING 2 +#define HDCP_KEYS_STATE_VALID 3 +#define HDCP_KEYS_STATE_AKSV_NOT_VALID 4 +#define HDCP_KEYS_STATE_CHKSUM_MISMATCH 5 +#define HDCP_KEYS_STATE_PROD_AKSV 6 +#define HDCP_KEYS_STATE_RESERVED 7 + +#define TZ_HDCP_CMD_ID 0x00004401 + +#define HDCP_INT_CLR (isr->auth_success_ack | isr->auth_fail_ack | \ + isr->auth_fail_info_ack | isr->tx_req_ack | \ + isr->encryption_ready_ack | \ + isr->encryption_not_ready_ack | isr->tx_req_done_ack) + +#define HDCP_INT_EN (isr->auth_success_mask | isr->auth_fail_mask | \ + isr->encryption_ready_mask | \ + isr->encryption_not_ready_mask) + +#define HDCP_POLL_SLEEP_US (20 * 1000) +#define HDCP_POLL_TIMEOUT_US (HDCP_POLL_SLEEP_US * 100) + +#define sde_hdcp_1x_state(x) (hdcp->hdcp_state == x) + +struct sde_hdcp_sink_addr { + char *name; + u32 addr; + u32 len; +}; + +struct sde_hdcp_1x_reg_data { + u32 reg_id; + struct sde_hdcp_sink_addr *sink; +}; + +struct sde_hdcp_skaddr_map { + /* addresses to read from sink */ + struct sde_hdcp_sink_addr bcaps; + struct sde_hdcp_sink_addr bksv; + struct sde_hdcp_sink_addr r0; + struct sde_hdcp_sink_addr bstatus; + struct sde_hdcp_sink_addr cp_irq_status; + struct sde_hdcp_sink_addr ksv_fifo; + struct sde_hdcp_sink_addr v_h0; + struct sde_hdcp_sink_addr v_h1; + struct sde_hdcp_sink_addr v_h2; + struct sde_hdcp_sink_addr v_h3; + struct sde_hdcp_sink_addr v_h4; + + /* addresses to write to sink */ + struct sde_hdcp_sink_addr an; + struct sde_hdcp_sink_addr aksv; + struct sde_hdcp_sink_addr ainfo; +}; + +struct sde_hdcp_int_set { + /* interrupt register */ + u32 int_reg; + + /* interrupt enable/disable masks */ + u32 auth_success_mask; + u32 auth_fail_mask; + u32 encryption_ready_mask; + u32 encryption_not_ready_mask; + u32 tx_req_mask; + u32 tx_req_done_mask; + + /* interrupt acknowledgment */ + u32 auth_success_ack; + u32 auth_fail_ack; + u32 auth_fail_info_ack; + u32 encryption_ready_ack; + u32 encryption_not_ready_ack; + u32 tx_req_ack; + u32 tx_req_done_ack; + + /* interrupt status */ + u32 auth_success_int; + u32 auth_fail_int; + u32 encryption_ready; + u32 encryption_not_ready; + u32 tx_req_int; + u32 tx_req_done_int; +}; + +struct sde_hdcp_reg_set { + u32 status; + u32 keys_offset; + u32 r0_offset; + u32 v_offset; + u32 ctrl; + u32 aksv_lsb; + u32 aksv_msb; + u32 entropy_ctrl0; + u32 entropy_ctrl1; + u32 sec_sha_ctrl; + u32 sec_sha_data; + u32 sha_status; + + u32 data2_0; + u32 data3; + u32 data4; + u32 data5; + u32 data6; + + u32 sec_data0; + u32 sec_data1; + u32 sec_data7; + u32 sec_data8; + u32 sec_data9; + u32 sec_data10; + u32 sec_data11; + u32 sec_data12; + + u32 reset; + u32 reset_bit; + + u32 repeater; +}; + +#define HDCP_REG_SET_CLIENT_HDMI \ + {HDMI_HDCP_LINK0_STATUS, 28, 24, 20, HDMI_HDCP_CTRL, \ + HDMI_HDCP_SW_LOWER_AKSV, HDMI_HDCP_SW_UPPER_AKSV, \ + HDMI_HDCP_ENTROPY_CTRL0, HDMI_HDCP_ENTROPY_CTRL1, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_SHA_CTRL, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_SHA_DATA, \ + HDMI_HDCP_SHA_STATUS, HDMI_HDCP_RCVPORT_DATA2_0, \ + HDMI_HDCP_RCVPORT_DATA3, HDMI_HDCP_RCVPORT_DATA4, \ + HDMI_HDCP_RCVPORT_DATA5, HDMI_HDCP_RCVPORT_DATA6, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA0, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA1, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA7, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA8, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA9, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA10, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA11, \ + HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, \ + HDMI_HDCP_RESET, BIT(0), BIT(6)} + +/* To do for DP */ +#define HDCP_REG_SET_CLIENT_DP \ + {0} + +#define HDCP_HDMI_SINK_ADDR_MAP \ + {{"bcaps", 0x40, 1}, {"bksv", 0x00, 5}, {"r0'", 0x08, 2}, \ + {"bstatus", 0x41, 2}, {"??", 0x0, 0}, {"ksv-fifo", 0x43, 0}, \ + {"v_h0", 0x20, 4}, {"v_h1", 0x24, 4}, {"v_h2", 0x28, 4}, \ + {"v_h3", 0x2c, 4}, {"v_h4", 0x30, 4}, {"an", 0x18, 8}, \ + {"aksv", 0x10, 5}, {"ainfo", 0x00, 0},} + +#define HDCP_DP_SINK_ADDR_MAP \ + {{"bcaps", 0x68028, 1}, {"bksv", 0x68000, 5}, {"r0'", 0x68005, 2}, \ + {"binfo", 0x6802A, 2}, {"cp_irq_status", 0x68029, 1}, \ + {"ksv-fifo", 0x6802C, 0}, {"v_h0", 0x68014, 4}, {"v_h1", 0x68018, 4}, \ + {"v_h2", 0x6801C, 4}, {"v_h3", 0x68020, 4}, {"v_h4", 0x68024, 4}, \ + {"an", 0x6800C, 8}, {"aksv", 0x68007, 5}, {"ainfo", 0x6803B, 1} } + +#define HDCP_HDMI_INT_SET \ + {HDMI_HDCP_INT_CTRL, \ + BIT(2), BIT(6), 0, 0, 0, 0, \ + BIT(1), BIT(5), BIT(7), 0, 0, 0, 0, \ + BIT(0), BIT(4), 0, 0, 0, 0} + +#define HDCP_DP_INT_SET \ + {DP_INTR_STATUS2, \ + BIT(17), BIT(20), BIT(24), BIT(27), 0, 0, \ + BIT(16), BIT(19), BIT(21), BIT(23), BIT(26), 0, 0, \ + BIT(15), BIT(18), BIT(22), BIT(25), 0, 0} + +struct sde_hdcp_1x { + u8 bcaps; + u32 tp_msgid; + u32 an_0, an_1, aksv_0, aksv_1; + bool sink_r0_ready; + bool reauth; + bool ksv_ready; + enum sde_hdcp_states hdcp_state; + struct HDCP_V2V1_MSG_TOPOLOGY cached_tp; + struct HDCP_V2V1_MSG_TOPOLOGY current_tp; + struct delayed_work hdcp_auth_work; + struct completion r0_checked; + struct completion sink_r0_available; + struct sde_hdcp_init_data init_data; + struct sde_hdcp_ops *ops; + struct sde_hdcp_reg_set reg_set; + struct sde_hdcp_int_set int_set; + struct sde_hdcp_skaddr_map sink_addr; + struct workqueue_struct *workq; +}; + +const char *sde_hdcp_state_name(enum sde_hdcp_states hdcp_state) +{ + switch (hdcp_state) { + case HDCP_STATE_INACTIVE: return "HDCP_STATE_INACTIVE"; + case HDCP_STATE_AUTHENTICATING: return "HDCP_STATE_AUTHENTICATING"; + case HDCP_STATE_AUTHENTICATED: return "HDCP_STATE_AUTHENTICATED"; + case HDCP_STATE_AUTH_FAIL: return "HDCP_STATE_AUTH_FAIL"; + default: return "???"; + } +} + +static int sde_hdcp_1x_count_one(u8 *array, u8 len) +{ + int i, j, count = 0; + + for (i = 0; i < len; i++) + for (j = 0; j < 8; j++) + count += (((array[i] >> j) & 0x1) ? 1 : 0); + return count; +} + +static void reset_hdcp_ddc_failures(struct sde_hdcp_1x *hdcp) +{ + int hdcp_ddc_ctrl1_reg; + int hdcp_ddc_status; + int failure; + int nack0; + struct dss_io_data *io; + + if (!hdcp || !hdcp->init_data.core_io) { + pr_err("invalid input\n"); + return; + } + + io = hdcp->init_data.core_io; + + /* Check for any DDC transfer failures */ + hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS); + failure = (hdcp_ddc_status >> 16) & BIT(0); + nack0 = (hdcp_ddc_status >> 14) & BIT(0); + SDE_HDCP_DEBUG("%s: HDCP_DDC_STATUS=0x%x, FAIL=%d, NACK0=%d\n", + SDE_HDCP_STATE_NAME, hdcp_ddc_status, failure, nack0); + + if (failure) { + /* + * Indicates that the last HDCP HW DDC transfer failed. + * This occurs when a transfer is attempted with HDCP DDC + * disabled (HDCP_DDC_DISABLE=1) or the number of retries + * matches HDCP_DDC_RETRY_CNT. + * Failure occurred, let's clear it. + */ + SDE_HDCP_DEBUG("%s: DDC failure HDCP_DDC_STATUS=0x%08x\n", + SDE_HDCP_STATE_NAME, hdcp_ddc_status); + + /* First, Disable DDC */ + DSS_REG_W(io, HDMI_HDCP_DDC_CTRL_0, BIT(0)); + + /* ACK the Failure to Clear it */ + hdcp_ddc_ctrl1_reg = DSS_REG_R(io, HDMI_HDCP_DDC_CTRL_1); + DSS_REG_W(io, HDMI_HDCP_DDC_CTRL_1, + hdcp_ddc_ctrl1_reg | BIT(0)); + + /* Check if the FAILURE got Cleared */ + hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS); + hdcp_ddc_status = (hdcp_ddc_status >> 16) & BIT(0); + if (hdcp_ddc_status == 0x0) + SDE_HDCP_DEBUG("%s: HDCP DDC Failure cleared\n", + SDE_HDCP_STATE_NAME); + else + SDE_ERROR("%s: Unable to clear HDCP DDC Failure", + SDE_HDCP_STATE_NAME); + + /* Re-Enable HDCP DDC */ + DSS_REG_W(io, HDMI_HDCP_DDC_CTRL_0, 0); + } + + if (nack0) { + SDE_HDCP_DEBUG("%s: Before: HDMI_DDC_SW_STATUS=0x%08x\n", + SDE_HDCP_STATE_NAME, DSS_REG_R(io, HDMI_DDC_SW_STATUS)); + /* Reset HDMI DDC software status */ + DSS_REG_W_ND(io, HDMI_DDC_CTRL, + DSS_REG_R(io, HDMI_DDC_CTRL) | BIT(3)); + msleep(20); + DSS_REG_W_ND(io, HDMI_DDC_CTRL, + DSS_REG_R(io, HDMI_DDC_CTRL) & ~(BIT(3))); + + /* Reset HDMI DDC Controller */ + DSS_REG_W_ND(io, HDMI_DDC_CTRL, + DSS_REG_R(io, HDMI_DDC_CTRL) | BIT(1)); + msleep(20); + DSS_REG_W_ND(io, HDMI_DDC_CTRL, + DSS_REG_R(io, HDMI_DDC_CTRL) & ~BIT(1)); + SDE_HDCP_DEBUG("%s: After: HDMI_DDC_SW_STATUS=0x%08x\n", + SDE_HDCP_STATE_NAME, DSS_REG_R(io, HDMI_DDC_SW_STATUS)); + } + + hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS); + + failure = (hdcp_ddc_status >> 16) & BIT(0); + nack0 = (hdcp_ddc_status >> 14) & BIT(0); + SDE_HDCP_DEBUG("%s: On Exit: HDCP_DDC_STATUS=0x%x, FAIL=%d, NACK0=%d\n", + SDE_HDCP_STATE_NAME, hdcp_ddc_status, failure, nack0); +} /* reset_hdcp_ddc_failures */ + +static void sde_hdcp_1x_hw_ddc_clean(struct sde_hdcp_1x *hdcp) +{ + struct dss_io_data *io = NULL; + u32 hdcp_ddc_status, ddc_hw_status; + u32 ddc_xfer_done, ddc_xfer_req; + u32 ddc_hw_req, ddc_hw_not_idle; + bool ddc_hw_not_ready, xfer_not_done, hw_not_done; + u32 timeout_count; + + if (!hdcp || !hdcp->init_data.core_io) { + pr_err("invalid input\n"); + return; + } + + io = hdcp->init_data.core_io; + if (!io->base) { + pr_err("core io not inititalized\n"); + return; + } + + /* Wait to be clean on DDC HW engine */ + timeout_count = 100; + do { + hdcp_ddc_status = DSS_REG_R(io, HDMI_HDCP_DDC_STATUS); + ddc_xfer_req = hdcp_ddc_status & BIT(4); + ddc_xfer_done = hdcp_ddc_status & BIT(10); + + ddc_hw_status = DSS_REG_R(io, HDMI_DDC_HW_STATUS); + ddc_hw_req = ddc_hw_status & BIT(16); + ddc_hw_not_idle = ddc_hw_status & (BIT(0) | BIT(1)); + + /* ddc transfer was requested but not completed */ + xfer_not_done = ddc_xfer_req && !ddc_xfer_done; + + /* ddc status is not idle or a hw request pending */ + hw_not_done = ddc_hw_not_idle || ddc_hw_req; + + ddc_hw_not_ready = xfer_not_done || hw_not_done; + + SDE_HDCP_DEBUG("%s: timeout count(%d): ddc hw%sready\n", + SDE_HDCP_STATE_NAME, timeout_count, + ddc_hw_not_ready ? " not " : " "); + SDE_HDCP_DEBUG("hdcp_ddc_status[0x%x], ddc_hw_status[0x%x]\n", + hdcp_ddc_status, ddc_hw_status); + if (ddc_hw_not_ready) + msleep(20); + } while (ddc_hw_not_ready && --timeout_count); +} /* hdcp_1x_hw_ddc_clean */ + +static int sde_hdcp_1x_load_keys(void *input) +{ + int rc = 0; + bool use_sw_keys = false; + u32 reg_val; + u32 ksv_lsb_addr, ksv_msb_addr; + u32 aksv_lsb, aksv_msb; + u8 aksv[5]; + struct dss_io_data *io; + struct dss_io_data *qfprom_io; + struct sde_hdcp_1x *hdcp = input; + struct sde_hdcp_reg_set *reg_set; + + if (!hdcp || !hdcp->init_data.core_io || + !hdcp->init_data.qfprom_io) { + pr_err("invalid input\n"); + rc = -EINVAL; + goto end; + } + + if (!sde_hdcp_1x_state(HDCP_STATE_INACTIVE) && + !sde_hdcp_1x_state(HDCP_STATE_AUTH_FAIL)) { + pr_err("%s: invalid state. returning\n", + SDE_HDCP_STATE_NAME); + rc = -EINVAL; + goto end; + } + + io = hdcp->init_data.core_io; + qfprom_io = hdcp->init_data.qfprom_io; + reg_set = &hdcp->reg_set; + + /* On compatible hardware, use SW keys */ + reg_val = DSS_REG_R(qfprom_io, SEC_CTRL_HW_VERSION); + if (reg_val >= HDCP_SEL_MIN_SEC_VERSION) { + reg_val = DSS_REG_R(qfprom_io, + QFPROM_RAW_FEAT_CONFIG_ROW0_MSB + + QFPROM_RAW_VERSION_4); + + if (!(reg_val & BIT(23))) + use_sw_keys = true; + } + + if (use_sw_keys) { + if (hdcp1_set_keys(&aksv_msb, &aksv_lsb)) { + pr_err("setting hdcp SW keys failed\n"); + rc = -EINVAL; + goto end; + } + } else { + /* Fetch aksv from QFPROM, this info should be public. */ + ksv_lsb_addr = HDCP_KSV_LSB; + ksv_msb_addr = HDCP_KSV_MSB; + + if (hdcp->init_data.sec_access) { + ksv_lsb_addr += HDCP_KSV_VERSION_4_OFFSET; + ksv_msb_addr += HDCP_KSV_VERSION_4_OFFSET; + } + + aksv_lsb = DSS_REG_R(qfprom_io, ksv_lsb_addr); + aksv_msb = DSS_REG_R(qfprom_io, ksv_msb_addr); + } + + SDE_HDCP_DEBUG("%s: AKSV=%02x%08x\n", SDE_HDCP_STATE_NAME, + aksv_msb, aksv_lsb); + + aksv[0] = aksv_lsb & 0xFF; + aksv[1] = (aksv_lsb >> 8) & 0xFF; + aksv[2] = (aksv_lsb >> 16) & 0xFF; + aksv[3] = (aksv_lsb >> 24) & 0xFF; + aksv[4] = aksv_msb & 0xFF; + + /* check there are 20 ones in AKSV */ + if (sde_hdcp_1x_count_one(aksv, 5) != 20) { + pr_err("AKSV bit count failed\n"); + rc = -EINVAL; + goto end; + } + + DSS_REG_W(io, reg_set->aksv_lsb, aksv_lsb); + DSS_REG_W(io, reg_set->aksv_msb, aksv_msb); + + /* Setup seed values for random number An */ + DSS_REG_W(io, reg_set->entropy_ctrl0, 0xB1FFB0FF); + DSS_REG_W(io, reg_set->entropy_ctrl1, 0xF00DFACE); + + /* make sure hw is programmed */ + wmb(); + + /* enable hdcp engine */ + DSS_REG_W(io, reg_set->ctrl, 0x1); + + hdcp->hdcp_state = HDCP_STATE_AUTHENTICATING; +end: + return rc; +} + +static int sde_hdcp_1x_read(struct sde_hdcp_1x *hdcp, + struct sde_hdcp_sink_addr *sink, + u8 *buf, bool realign) +{ + u32 rc = 0; + struct sde_hdmi_tx_ddc_data *ddc_data; + struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl; + + if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) { + reset_hdcp_ddc_failures(hdcp); + + ddc_ctrl = hdcp->init_data.ddc_ctrl; + ddc_data = &ddc_ctrl->ddc_data; + if (!ddc_data) { + SDE_ERROR("invalid ddc data\n"); + return -EINVAL; + } + memset(ddc_data, 0, sizeof(*ddc_data)); + ddc_data->dev_addr = 0x74; + ddc_data->offset = sink->addr; + ddc_data->data_buf = buf; + ddc_data->data_len = sink->len; + ddc_data->request_len = sink->len; + ddc_data->retry = 5; + ddc_data->what = sink->name; + ddc_data->retry_align = realign; + + rc = sde_hdmi_ddc_read((void *)hdcp->init_data.cb_data); + if (rc) + SDE_ERROR("%s: %s read failed\n", + SDE_HDCP_STATE_NAME, sink->name); + } else if (hdcp->init_data.client_id == HDCP_CLIENT_DP) { + /* To-do DP APIs go here */ + } + + return rc; +} + +static int sde_hdcp_1x_write(struct sde_hdcp_1x *hdcp, + struct sde_hdcp_sink_addr *sink, u8 *buf) +{ + int rc = 0; + struct sde_hdmi_tx_ddc_data *ddc_data; + struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl; + + if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) { + ddc_ctrl = hdcp->init_data.ddc_ctrl; + ddc_data = &ddc_ctrl->ddc_data; + + if (!ddc_data) { + SDE_ERROR("invalid ddc data\n"); + return -EINVAL; + } + memset(ddc_data, 0, sizeof(*ddc_data)); + + ddc_data->dev_addr = 0x74; + ddc_data->offset = sink->addr; + ddc_data->data_buf = buf; + ddc_data->data_len = sink->len; + ddc_data->what = sink->name; + + rc = sde_hdmi_ddc_write((void *)hdcp->init_data.cb_data); + if (rc) + SDE_ERROR("%s: %s write failed\n", + SDE_HDCP_STATE_NAME, sink->name); + } else if (hdcp->init_data.client_id == HDCP_CLIENT_DP) { + /* To-do DP APIs go here */ + } + + return rc; +} + +static void sde_hdcp_1x_enable_interrupts(struct sde_hdcp_1x *hdcp) +{ + u32 intr_reg; + struct dss_io_data *io; + struct sde_hdcp_int_set *isr; + + io = hdcp->init_data.core_io; + isr = &hdcp->int_set; + + intr_reg = DSS_REG_R(io, isr->int_reg); + + intr_reg |= HDCP_INT_CLR | HDCP_INT_EN; + + DSS_REG_W(io, isr->int_reg, intr_reg); +} + +static int sde_hdcp_1x_read_bcaps(struct sde_hdcp_1x *hdcp) +{ + int rc; + struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set; + struct dss_io_data *hdcp_io = hdcp->init_data.hdcp_io; + + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + pr_err("invalid state\n"); + return -EINVAL; + } + + rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.bcaps, + &hdcp->bcaps, false); + if (IS_ERR_VALUE(rc)) { + pr_err("error reading bcaps\n"); + goto error; + } + + SDE_HDCP_DEBUG("bcaps read: 0x%x\n", hdcp->bcaps); + + hdcp->current_tp.ds_type = hdcp->bcaps & reg_set->repeater ? + DS_REPEATER : DS_RECEIVER; + + SDE_HDCP_DEBUG("ds: %s\n", hdcp->current_tp.ds_type == DS_REPEATER ? + "repeater" : "receiver"); + + /* Write BCAPS to the hardware */ + DSS_REG_W(hdcp_io, reg_set->sec_data12, hdcp->bcaps); +error: + return rc; +} + +static int sde_hdcp_1x_wait_for_hw_ready(struct sde_hdcp_1x *hdcp) +{ + int rc; + u32 link0_status; + struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set; + struct dss_io_data *io = hdcp->init_data.core_io; + + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + pr_err("invalid state\n"); + return -EINVAL; + } + + /* Wait for HDCP keys to be checked and validated */ + rc = readl_poll_timeout(io->base + reg_set->status, link0_status, + ((link0_status >> reg_set->keys_offset) & 0x7) + == HDCP_KEYS_STATE_VALID || + !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING), + HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US); + if (IS_ERR_VALUE(rc)) { + pr_err("key not ready\n"); + goto error; + } + + /* + * 1.1_Features turned off by default. + * No need to write AInfo since 1.1_Features is disabled. + */ + DSS_REG_W(io, reg_set->data4, 0); + + /* Wait for An0 and An1 bit to be ready */ + rc = readl_poll_timeout(io->base + reg_set->status, link0_status, + (link0_status & (BIT(8) | BIT(9))) || + !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING), + HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US); + if (IS_ERR_VALUE(rc)) { + pr_err("An not ready\n"); + goto error; + } + + /* As per hardware recommendations, wait before reading An */ + msleep(20); +error: + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) + rc = -EINVAL; + + return rc; +} + +static int sde_hdcp_1x_send_an_aksv_to_sink(struct sde_hdcp_1x *hdcp) +{ + int rc; + u8 an[8], aksv[5]; + + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + pr_err("invalid state\n"); + return -EINVAL; + } + + an[0] = hdcp->an_0 & 0xFF; + an[1] = (hdcp->an_0 >> 8) & 0xFF; + an[2] = (hdcp->an_0 >> 16) & 0xFF; + an[3] = (hdcp->an_0 >> 24) & 0xFF; + an[4] = hdcp->an_1 & 0xFF; + an[5] = (hdcp->an_1 >> 8) & 0xFF; + an[6] = (hdcp->an_1 >> 16) & 0xFF; + an[7] = (hdcp->an_1 >> 24) & 0xFF; + + SDE_HDCP_DEBUG("an read: 0x%2x%2x%2x%2x%2x%2x%2x%2x\n", + an[7], an[6], an[5], an[4], an[3], an[2], an[1], an[0]); + + rc = sde_hdcp_1x_write(hdcp, &hdcp->sink_addr.an, an); + if (IS_ERR_VALUE(rc)) { + pr_err("error writing an to sink\n"); + goto error; + } + + /* Copy An and AKSV to byte arrays for transmission */ + aksv[0] = hdcp->aksv_0 & 0xFF; + aksv[1] = (hdcp->aksv_0 >> 8) & 0xFF; + aksv[2] = (hdcp->aksv_0 >> 16) & 0xFF; + aksv[3] = (hdcp->aksv_0 >> 24) & 0xFF; + aksv[4] = hdcp->aksv_1 & 0xFF; + + SDE_HDCP_DEBUG("aksv read: 0x%2x%2x%2x%2x%2x\n", + aksv[4], aksv[3], aksv[2], aksv[1], aksv[0]); + + rc = sde_hdcp_1x_write(hdcp, &hdcp->sink_addr.aksv, aksv); + if (IS_ERR_VALUE(rc)) { + pr_err("error writing aksv to sink\n"); + goto error; + } +error: + return rc; +} + +static int sde_hdcp_1x_read_an_aksv_from_hw(struct sde_hdcp_1x *hdcp) +{ + struct dss_io_data *io = hdcp->init_data.core_io; + struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set; + + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + pr_err("invalid state\n"); + return -EINVAL; + } + + hdcp->an_0 = DSS_REG_R(io, reg_set->data5); + if (hdcp->init_data.client_id == HDCP_CLIENT_DP) { + udelay(1); + hdcp->an_0 = DSS_REG_R(io, reg_set->data5); + } + + hdcp->an_1 = DSS_REG_R(io, reg_set->data6); + if (hdcp->init_data.client_id == HDCP_CLIENT_DP) { + udelay(1); + hdcp->an_1 = DSS_REG_R(io, reg_set->data6); + } + + /* Read AKSV */ + hdcp->aksv_0 = DSS_REG_R(io, reg_set->data3); + hdcp->aksv_1 = DSS_REG_R(io, reg_set->data4); + + return 0; +} + +static int sde_hdcp_1x_get_bksv_from_sink(struct sde_hdcp_1x *hdcp) +{ + int rc; + u8 *bksv = hdcp->current_tp.bksv; + u32 link0_bksv_0, link0_bksv_1; + struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set; + struct dss_io_data *hdcp_io = hdcp->init_data.hdcp_io; + + rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.bksv, bksv, false); + if (IS_ERR_VALUE(rc)) { + pr_err("error reading bksv from sink\n"); + goto error; + } + + SDE_HDCP_DEBUG("bksv read: 0x%2x%2x%2x%2x%2x\n", + bksv[4], bksv[3], bksv[2], bksv[1], bksv[0]); + + /* check there are 20 ones in BKSV */ + if (sde_hdcp_1x_count_one(bksv, 5) != 20) { + pr_err("%s: BKSV doesn't have 20 1's and 20 0's\n", + SDE_HDCP_STATE_NAME); + rc = -EINVAL; + goto error; + } + + link0_bksv_0 = bksv[3]; + link0_bksv_0 = (link0_bksv_0 << 8) | bksv[2]; + link0_bksv_0 = (link0_bksv_0 << 8) | bksv[1]; + link0_bksv_0 = (link0_bksv_0 << 8) | bksv[0]; + link0_bksv_1 = bksv[4]; + + DSS_REG_W(hdcp_io, reg_set->sec_data0, link0_bksv_0); + DSS_REG_W(hdcp_io, reg_set->sec_data1, link0_bksv_1); +error: + return rc; +} + +static void sde_hdcp_1x_enable_sink_irq_hpd(struct sde_hdcp_1x *hdcp) +{ + int rc; + u8 enable_hpd_irq = 0x1; + + if (hdcp->current_tp.ds_type != DS_REPEATER) + return; + + rc = sde_hdcp_1x_write(hdcp, &hdcp->sink_addr.ainfo, &enable_hpd_irq); + if (IS_ERR_VALUE(rc)) + SDE_HDCP_DEBUG("error writing ainfo to sink\n"); +} + +static int sde_hdcp_1x_verify_r0(struct sde_hdcp_1x *hdcp) +{ + int rc, r0_retry = 3; + u8 buf[2]; + u32 link0_status, timeout_count; + u32 const r0_read_delay_us = 1; + u32 const r0_read_timeout_us = r0_read_delay_us * 10; + struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set; + struct dss_io_data *io = hdcp->init_data.core_io; + + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + pr_err("invalid state\n"); + return -EINVAL; + } + + /* Wait for HDCP R0 computation to be completed */ + rc = readl_poll_timeout(io->base + reg_set->status, link0_status, + (link0_status & BIT(reg_set->r0_offset)) || + !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING), + HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US); + if (IS_ERR_VALUE(rc)) { + pr_err("R0 not ready\n"); + goto error; + } + + /* + * HDCP Compliace Test case 1A-01: + * Wait here at least 100ms before reading R0' + */ + if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) { + msleep(100); + } else { + if (!hdcp->sink_r0_ready) { + reinit_completion(&hdcp->sink_r0_available); + timeout_count = wait_for_completion_timeout( + &hdcp->sink_r0_available, HZ / 2); + + if (hdcp->reauth) { + pr_err("sink R0 not ready\n"); + rc = -EINVAL; + goto error; + } + } + } + + do { + memset(buf, 0, sizeof(buf)); + + rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.r0, + buf, false); + if (IS_ERR_VALUE(rc)) { + pr_err("error reading R0' from sink\n"); + goto error; + } + + SDE_HDCP_DEBUG("sink R0'read: %2x%2x\n", buf[1], buf[0]); + + DSS_REG_W(io, reg_set->data2_0, (((u32)buf[1]) << 8) | buf[0]); + + rc = readl_poll_timeout(io->base + reg_set->status, + link0_status, (link0_status & BIT(12)) || + !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING), + r0_read_delay_us, r0_read_timeout_us); + } while (rc && --r0_retry); +error: + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) + rc = -EINVAL; + + return rc; +} + +static int sde_hdcp_1x_authentication_part1(struct sde_hdcp_1x *hdcp) +{ + int rc; + + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + pr_err("invalid state\n"); + return -EINVAL; + } + + sde_hdcp_1x_enable_interrupts(hdcp); + + rc = sde_hdcp_1x_read_bcaps(hdcp); + if (rc) + goto error; + + rc = sde_hdcp_1x_wait_for_hw_ready(hdcp); + if (rc) + goto error; + + rc = sde_hdcp_1x_read_an_aksv_from_hw(hdcp); + if (rc) + goto error; + + rc = sde_hdcp_1x_get_bksv_from_sink(hdcp); + if (rc) + goto error; + + rc = sde_hdcp_1x_send_an_aksv_to_sink(hdcp); + if (rc) + goto error; + + sde_hdcp_1x_enable_sink_irq_hpd(hdcp); + + rc = sde_hdcp_1x_verify_r0(hdcp); + if (rc) + goto error; + + pr_info("SUCCESSFUL\n"); + + return 0; +error: + pr_err("%s: FAILED\n", SDE_HDCP_STATE_NAME); + + return rc; +} + +static int sde_hdcp_1x_transfer_v_h(struct sde_hdcp_1x *hdcp) +{ + int rc = 0; + struct dss_io_data *io = hdcp->init_data.hdcp_io; + struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set; + struct sde_hdcp_1x_reg_data reg_data[] = { + {reg_set->sec_data7, &hdcp->sink_addr.v_h0}, + {reg_set->sec_data8, &hdcp->sink_addr.v_h1}, + {reg_set->sec_data9, &hdcp->sink_addr.v_h2}, + {reg_set->sec_data10, &hdcp->sink_addr.v_h3}, + {reg_set->sec_data11, &hdcp->sink_addr.v_h4}, + }; + struct sde_hdcp_sink_addr sink = {"V", reg_data->sink->addr}; + u32 size = ARRAY_SIZE(reg_data); + u8 buf[0xFF] = {0}; + u32 i = 0, len = 0; + + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + pr_err("invalid state\n"); + return -EINVAL; + } + + for (i = 0; i < size; i++) { + struct sde_hdcp_1x_reg_data *rd = reg_data + i; + + len += rd->sink->len; + } + + sink.len = len; + + rc = sde_hdcp_1x_read(hdcp, &sink, buf, false); + if (IS_ERR_VALUE(rc)) { + pr_err("error reading %s\n", sink.name); + goto end; + } + + + for (i = 0; i < size; i++) { + struct sde_hdcp_1x_reg_data *rd = reg_data + i; + u32 reg_data; + + memcpy(®_data, buf + (sizeof(u32) * i), sizeof(u32)); + DSS_REG_W(io, rd->reg_id, reg_data); + } +end: + return rc; +} + +static int sde_hdcp_1x_validate_downstream(struct sde_hdcp_1x *hdcp) +{ + int rc; + u8 buf[2] = {0, 0}; + u8 device_count, depth; + u8 max_cascade_exceeded, max_devs_exceeded; + u16 bstatus; + struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set; + + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + pr_err("invalid state\n"); + return -EINVAL; + } + + rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.bstatus, + buf, false); + if (IS_ERR_VALUE(rc)) { + pr_err("error reading bstatus\n"); + goto end; + } + + bstatus = buf[1]; + bstatus = (bstatus << 8) | buf[0]; + + device_count = bstatus & 0x7F; + + SDE_HDCP_DEBUG("device count %d\n", device_count); + + /* Cascaded repeater depth */ + depth = (bstatus >> 8) & 0x7; + SDE_HDCP_DEBUG("depth %d\n", depth); + + /* + * HDCP Compliance 1B-05: + * Check if no. of devices connected to repeater + * exceed max_devices_connected from bit 7 of Bstatus. + */ + max_devs_exceeded = (bstatus & BIT(7)) >> 7; + if (max_devs_exceeded == 0x01) { + pr_err("no. of devs connected exceed max allowed\n"); + rc = -EINVAL; + goto end; + } + + /* + * HDCP Compliance 1B-06: + * Check if no. of cascade connected to repeater + * exceed max_cascade_connected from bit 11 of Bstatus. + */ + max_cascade_exceeded = (bstatus & BIT(11)) >> 11; + if (max_cascade_exceeded == 0x01) { + pr_err("no. of cascade connections exceed max allowed\n"); + rc = -EINVAL; + goto end; + } + + /* Update topology information */ + hdcp->current_tp.dev_count = device_count; + hdcp->current_tp.max_cascade_exceeded = max_cascade_exceeded; + hdcp->current_tp.max_dev_exceeded = max_devs_exceeded; + hdcp->current_tp.depth = depth; + + DSS_REG_W(hdcp->init_data.hdcp_io, + reg_set->sec_data12, hdcp->bcaps | (bstatus << 8)); +end: + return rc; +} + +static int sde_hdcp_1x_read_ksv_fifo(struct sde_hdcp_1x *hdcp) +{ + u32 ksv_read_retry = 20, ksv_bytes, rc = 0; + u8 *ksv_fifo = hdcp->current_tp.ksv_list; + + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + pr_err("invalid state\n"); + return -EINVAL; + } + + memset(ksv_fifo, 0, sizeof(hdcp->current_tp.ksv_list)); + + /* each KSV is 5 bytes long */ + ksv_bytes = 5 * hdcp->current_tp.dev_count; + hdcp->sink_addr.ksv_fifo.len = ksv_bytes; + + while (ksv_bytes && --ksv_read_retry) { + rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.ksv_fifo, + ksv_fifo, true); + if (IS_ERR_VALUE(rc)) + pr_err("could not read ksv fifo (%d)\n", + ksv_read_retry); + else + break; + } + + if (rc) + pr_err("error reading ksv_fifo\n"); + + return rc; +} + +static int sde_hdcp_1x_write_ksv_fifo(struct sde_hdcp_1x *hdcp) +{ + int i, rc = 0; + u8 *ksv_fifo = hdcp->current_tp.ksv_list; + u32 ksv_bytes = hdcp->sink_addr.ksv_fifo.len; + struct dss_io_data *io = hdcp->init_data.core_io; + struct dss_io_data *sec_io = hdcp->init_data.hdcp_io; + struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set; + u32 sha_status = 0, status; + + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + pr_err("invalid state\n"); + return -EINVAL; + } + + /* reset SHA Controller */ + DSS_REG_W(sec_io, reg_set->sec_sha_ctrl, 0x1); + DSS_REG_W(sec_io, reg_set->sec_sha_ctrl, 0x0); + + for (i = 0; i < ksv_bytes - 1; i++) { + /* Write KSV byte and do not set DONE bit[0] */ + DSS_REG_W_ND(sec_io, reg_set->sec_sha_data, ksv_fifo[i] << 16); + + /* + * Once 64 bytes have been written, we need to poll for + * HDCP_SHA_BLOCK_DONE before writing any further + */ + if (i && !((i + 1) % 64)) { + rc = readl_poll_timeout(io->base + reg_set->sha_status, + sha_status, (sha_status & BIT(0)) || + !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING), + HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US); + if (IS_ERR_VALUE(rc)) { + pr_err("block not done\n"); + goto error; + } + } + } + + /* Write l to DONE bit[0] */ + DSS_REG_W_ND(sec_io, reg_set->sec_sha_data, + (ksv_fifo[ksv_bytes - 1] << 16) | 0x1); + + /* Now wait for HDCP_SHA_COMP_DONE */ + rc = readl_poll_timeout(io->base + reg_set->sha_status, sha_status, + (sha_status & BIT(4)) || + !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING), + HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US); + if (IS_ERR_VALUE(rc)) { + pr_err("V computation not done\n"); + goto error; + } + + /* Wait for V_MATCHES */ + rc = readl_poll_timeout(io->base + reg_set->status, status, + (status & BIT(reg_set->v_offset)) || + !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING), + HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US); + if (IS_ERR_VALUE(rc)) { + pr_err("V mismatch\n"); + rc = -EINVAL; + } +error: + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) + rc = -EINVAL; + + return rc; +} + +static int sde_hdcp_1x_wait_for_ksv_ready(struct sde_hdcp_1x *hdcp) +{ + int rc, timeout; + + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + pr_err("invalid state\n"); + return -EINVAL; + } + + /* + * Wait until READY bit is set in BCAPS, as per HDCP specifications + * maximum permitted time to check for READY bit is five seconds. + */ + rc = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.bcaps, + &hdcp->bcaps, false); + if (IS_ERR_VALUE(rc)) { + pr_err("error reading bcaps\n"); + goto error; + } + + if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) { + timeout = 50; + + while (!(hdcp->bcaps & BIT(5)) && --timeout) { + rc = sde_hdcp_1x_read(hdcp, + &hdcp->sink_addr.bcaps, + &hdcp->bcaps, false); + if (IS_ERR_VALUE(rc) || + !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + pr_err("error reading bcaps\n"); + goto error; + } + msleep(100); + } + } else { + u8 cp_buf = 0; + struct sde_hdcp_sink_addr *sink = + &hdcp->sink_addr.cp_irq_status; + + timeout = jiffies_to_msecs(jiffies); + + while (1) { + rc = sde_hdcp_1x_read(hdcp, sink, &cp_buf, false); + if (rc) + goto error; + + if (cp_buf & BIT(0)) + break; + + /* max timeout of 5 sec as per hdcp 1.x spec */ + if (abs(timeout - jiffies_to_msecs(jiffies)) > 5000) { + timeout = 0; + break; + } + + if (hdcp->ksv_ready || hdcp->reauth || + !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) + break; + + /* re-read after a minimum delay */ + msleep(20); + } + } + + if (!timeout || hdcp->reauth || + !sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + pr_err("DS KSV not ready\n"); + rc = -EINVAL; + } else { + hdcp->ksv_ready = true; + } +error: + return rc; +} + +static int sde_hdcp_1x_authentication_part2(struct sde_hdcp_1x *hdcp) +{ + int rc; + int v_retry = 3; + + rc = sde_hdcp_1x_validate_downstream(hdcp); + if (rc) + goto error; + + rc = sde_hdcp_1x_read_ksv_fifo(hdcp); + if (rc) + goto error; + + do { + rc = sde_hdcp_1x_transfer_v_h(hdcp); + if (rc) + goto error; + + /* do not proceed further if no device connected */ + if (!hdcp->current_tp.dev_count) + goto error; + + rc = sde_hdcp_1x_write_ksv_fifo(hdcp); + } while (--v_retry && rc); +error: + if (rc) { + pr_err("%s: FAILED\n", SDE_HDCP_STATE_NAME); + } else { + hdcp->hdcp_state = HDCP_STATE_AUTHENTICATED; + + pr_info("SUCCESSFUL\n"); + } + + return rc; +} + +static void sde_hdcp_1x_cache_topology(struct sde_hdcp_1x *hdcp) +{ + if (!hdcp || !hdcp->init_data.core_io) { + pr_err("invalid input\n"); + return; + } + + memcpy((void *)&hdcp->cached_tp, + (void *) &hdcp->current_tp, + sizeof(hdcp->cached_tp)); + hdcp1_cache_repeater_topology((void *)&hdcp->cached_tp); +} + +static void sde_hdcp_1x_notify_topology(void) +{ + hdcp1_notify_topology(); +} + +static void sde_hdcp_1x_update_auth_status(struct sde_hdcp_1x *hdcp) +{ + if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATED)) { + sde_hdcp_1x_cache_topology(hdcp); + sde_hdcp_1x_notify_topology(); + } + + if (hdcp->init_data.notify_status && + !sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) { + hdcp->init_data.notify_status( + hdcp->init_data.cb_data, + hdcp->hdcp_state); + } +} + +static void sde_hdcp_1x_auth_work(struct work_struct *work) +{ + int rc; + struct delayed_work *dw = to_delayed_work(work); + struct sde_hdcp_1x *hdcp = container_of(dw, + struct sde_hdcp_1x, hdcp_auth_work); + struct dss_io_data *io; + + if (!hdcp) { + pr_err("invalid input\n"); + return; + } + + if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + pr_err("invalid state\n"); + return; + } + + hdcp->sink_r0_ready = false; + hdcp->reauth = false; + hdcp->ksv_ready = false; + + io = hdcp->init_data.core_io; + /* Enabling Software DDC for HDMI and REF timer for DP */ + if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) + DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION, DSS_REG_R(io, + HDMI_DDC_ARBITRATION) & ~(BIT(4))); + else if (hdcp->init_data.client_id == HDCP_CLIENT_DP) { + /* To do for DP */ + } + + /* + * program hw to enable encryption as soon as + * authentication is successful. + */ + hdcp1_set_enc(true); + + rc = sde_hdcp_1x_authentication_part1(hdcp); + if (rc) + goto end; + + if (hdcp->current_tp.ds_type == DS_REPEATER) { + rc = sde_hdcp_1x_wait_for_ksv_ready(hdcp); + if (rc) + goto end; + } else { + hdcp->hdcp_state = HDCP_STATE_AUTHENTICATED; + goto end; + } + + hdcp->ksv_ready = false; + + rc = sde_hdcp_1x_authentication_part2(hdcp); + if (rc) + goto end; + + /* + * Disabling software DDC before going into part3 to make sure + * there is no Arbitration between software and hardware for DDC + */ + if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) + DSS_REG_W_ND(io, HDMI_DDC_ARBITRATION, DSS_REG_R(io, + HDMI_DDC_ARBITRATION) | (BIT(4))); +end: + if (rc && !sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) + hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL; + + sde_hdcp_1x_update_auth_status(hdcp); +} + +static int sde_hdcp_1x_authenticate(void *input) +{ + struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input; + + if (!hdcp) { + pr_err("invalid input\n"); + return -EINVAL; + } + + flush_delayed_work(&hdcp->hdcp_auth_work); + + if (!sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) { + pr_err("invalid state\n"); + return -EINVAL; + } + + if (!sde_hdcp_1x_load_keys(input)) { + + queue_delayed_work(hdcp->workq, + &hdcp->hdcp_auth_work, HZ/2); + } else { + hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL; + sde_hdcp_1x_update_auth_status(hdcp); + } + + return 0; +} /* hdcp_1x_authenticate */ + +static int sde_hdcp_1x_reauthenticate(void *input) +{ + struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input; + struct dss_io_data *io; + struct sde_hdcp_reg_set *reg_set; + struct sde_hdcp_int_set *isr; + u32 hdmi_hw_version; + u32 ret = 0, reg; + + if (!hdcp || !hdcp->init_data.core_io) { + pr_err("invalid input\n"); + return -EINVAL; + } + + io = hdcp->init_data.core_io; + reg_set = &hdcp->reg_set; + isr = &hdcp->int_set; + + if (!sde_hdcp_1x_state(HDCP_STATE_AUTH_FAIL)) { + pr_err("invalid state\n"); + return -EINVAL; + } + + if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) { + hdmi_hw_version = DSS_REG_R(io, HDMI_VERSION); + if (hdmi_hw_version >= 0x30030000) { + DSS_REG_W(io, HDMI_CTRL_SW_RESET, BIT(1)); + DSS_REG_W(io, HDMI_CTRL_SW_RESET, 0); + } + + /* Wait to be clean on DDC HW engine */ + sde_hdcp_1x_hw_ddc_clean(hdcp); + } + + /* Disable HDCP interrupts */ + DSS_REG_W(io, isr->int_reg, DSS_REG_R(io, isr->int_reg) & ~HDCP_INT_EN); + + reg = DSS_REG_R(io, reg_set->reset); + DSS_REG_W(io, reg_set->reset, reg | reg_set->reset_bit); + + /* Disable encryption and disable the HDCP block */ + DSS_REG_W(io, reg_set->ctrl, 0); + + DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit); + + hdcp->hdcp_state = HDCP_STATE_INACTIVE; + sde_hdcp_1x_authenticate(hdcp); + + return ret; +} /* hdcp_1x_reauthenticate */ + +static void sde_hdcp_1x_off(void *input) +{ + struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input; + struct dss_io_data *io; + struct sde_hdcp_reg_set *reg_set; + struct sde_hdcp_int_set *isr; + int rc = 0; + u32 reg; + + if (!hdcp || !hdcp->init_data.core_io) { + pr_err("invalid input\n"); + return; + } + + io = hdcp->init_data.core_io; + reg_set = &hdcp->reg_set; + isr = &hdcp->int_set; + + if (sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) { + pr_err("invalid state\n"); + return; + } + + /* + * Disable HDCP interrupts. + * Also, need to set the state to inactive here so that any ongoing + * reauth works will know that the HDCP session has been turned off. + */ + mutex_lock(hdcp->init_data.mutex); + DSS_REG_W(io, isr->int_reg, + DSS_REG_R(io, isr->int_reg) & ~HDCP_INT_EN); + hdcp->hdcp_state = HDCP_STATE_INACTIVE; + mutex_unlock(hdcp->init_data.mutex); + + /* complete any wait pending */ + complete_all(&hdcp->sink_r0_available); + complete_all(&hdcp->r0_checked); + /* + * Cancel any pending auth/reauth attempts. + * If one is ongoing, this will wait for it to finish. + * No more reauthentiaction attempts will be scheduled since we + * set the currect state to inactive. + */ + rc = cancel_delayed_work_sync(&hdcp->hdcp_auth_work); + if (rc) + SDE_HDCP_DEBUG("%s: Deleted hdcp auth work\n", + SDE_HDCP_STATE_NAME); + + hdcp1_set_enc(false); + + reg = DSS_REG_R(io, reg_set->reset); + DSS_REG_W(io, reg_set->reset, reg | reg_set->reset_bit); + + /* Disable encryption and disable the HDCP block */ + DSS_REG_W(io, reg_set->ctrl, 0); + + DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit); + + hdcp->sink_r0_ready = false; + + SDE_HDCP_DEBUG("%s: HDCP: Off\n", SDE_HDCP_STATE_NAME); +} /* hdcp_1x_off */ + +static int sde_hdcp_1x_isr(void *input) +{ + struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input; + int rc = 0; + struct dss_io_data *io; + u32 hdcp_int_val; + struct sde_hdcp_reg_set *reg_set; + struct sde_hdcp_int_set *isr; + + if (!hdcp || !hdcp->init_data.core_io) { + pr_err("invalid input\n"); + rc = -EINVAL; + goto error; + } + + io = hdcp->init_data.core_io; + reg_set = &hdcp->reg_set; + isr = &hdcp->int_set; + + hdcp_int_val = DSS_REG_R(io, isr->int_reg); + + /* Ignore HDCP interrupts if HDCP is disabled */ + if (sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) { + DSS_REG_W(io, isr->int_reg, hdcp_int_val | HDCP_INT_CLR); + return 0; + } + + if (hdcp_int_val & isr->auth_success_int) { + /* AUTH_SUCCESS_INT */ + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->auth_success_ack)); + SDE_HDCP_DEBUG("%s: AUTH SUCCESS\n", SDE_HDCP_STATE_NAME); + + if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) + complete_all(&hdcp->r0_checked); + } + + if (hdcp_int_val & isr->auth_fail_int) { + /* AUTH_FAIL_INT */ + u32 link_status = DSS_REG_R(io, reg_set->status); + + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->auth_fail_ack)); + + SDE_HDCP_DEBUG("%s: AUTH FAIL, LINK0_STATUS=0x%08x\n", + SDE_HDCP_STATE_NAME, link_status); + + if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATED)) { + hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL; + sde_hdcp_1x_update_auth_status(hdcp); + } else if (sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) { + complete_all(&hdcp->r0_checked); + } + + /* Clear AUTH_FAIL_INFO as well */ + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->auth_fail_info_ack)); + } + + if (hdcp_int_val & isr->tx_req_int) { + /* DDC_XFER_REQ_INT */ + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->tx_req_ack)); + SDE_HDCP_DEBUG("%s: DDC_XFER_REQ_INT received\n", + SDE_HDCP_STATE_NAME); + } + + if (hdcp_int_val & isr->tx_req_done_int) { + /* DDC_XFER_DONE_INT */ + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->tx_req_done_ack)); + SDE_HDCP_DEBUG("%s: DDC_XFER_DONE received\n", + SDE_HDCP_STATE_NAME); + } + + if (hdcp_int_val & isr->encryption_ready) { + /* Encryption enabled */ + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->encryption_ready_ack)); + SDE_HDCP_DEBUG("%s: encryption ready received\n", + SDE_HDCP_STATE_NAME); + } + + if (hdcp_int_val & isr->encryption_not_ready) { + /* Encryption enabled */ + DSS_REG_W(io, isr->int_reg, + (hdcp_int_val | isr->encryption_not_ready_ack)); + SDE_HDCP_DEBUG("%s: encryption not ready received\n", + SDE_HDCP_STATE_NAME); + } + +error: + return rc; +} + +void sde_hdcp_1x_deinit(void *input) +{ + struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input; + + if (!hdcp) { + pr_err("invalid input\n"); + return; + } + + if (hdcp->workq) + destroy_workqueue(hdcp->workq); + + kfree(hdcp); +} /* hdcp_1x_deinit */ + +static void sde_hdcp_1x_update_client_reg_set(struct sde_hdcp_1x *hdcp) +{ + + if (hdcp->init_data.client_id == HDCP_CLIENT_HDMI) { + struct sde_hdcp_reg_set reg_set = HDCP_REG_SET_CLIENT_HDMI; + struct sde_hdcp_skaddr_map sink_addr = HDCP_HDMI_SINK_ADDR_MAP; + struct sde_hdcp_int_set isr = HDCP_HDMI_INT_SET; + + hdcp->reg_set = reg_set; + hdcp->sink_addr = sink_addr; + hdcp->int_set = isr; + } else if (hdcp->init_data.client_id == HDCP_CLIENT_DP) { + /* TO DO for DP + * Will be filled later + */ + } +} + +static bool sde_hdcp_1x_is_cp_irq_raised(struct sde_hdcp_1x *hdcp) +{ + int ret; + u8 buf = 0; + struct sde_hdcp_sink_addr sink = {"irq", 0x201, 1}; + + ret = sde_hdcp_1x_read(hdcp, &sink, &buf, false); + if (IS_ERR_VALUE(ret)) + pr_err("error reading irq_vector\n"); + + return buf & BIT(2) ? true : false; +} + +static void sde_hdcp_1x_clear_cp_irq(struct sde_hdcp_1x *hdcp) +{ + int ret; + u8 buf = BIT(2); + struct sde_hdcp_sink_addr sink = {"irq", 0x201, 1}; + + ret = sde_hdcp_1x_write(hdcp, &sink, &buf); + if (IS_ERR_VALUE(ret)) + pr_err("error clearing irq_vector\n"); +} + +static int sde_hdcp_1x_cp_irq(void *input) +{ + struct sde_hdcp_1x *hdcp = (struct sde_hdcp_1x *)input; + u8 buf = 0; + int ret; + + if (!hdcp) { + pr_err("invalid input\n"); + goto irq_not_handled; + } + + if (!sde_hdcp_1x_is_cp_irq_raised(hdcp)) { + SDE_HDCP_DEBUG("cp_irq not raised\n"); + goto irq_not_handled; + } + + ret = sde_hdcp_1x_read(hdcp, &hdcp->sink_addr.cp_irq_status, + &buf, false); + if (IS_ERR_VALUE(ret)) { + pr_err("error reading cp_irq_status\n"); + goto irq_not_handled; + } + + if ((buf & BIT(2)) || (buf & BIT(3))) { + pr_err("%s\n", + buf & BIT(2) ? "LINK_INTEGRITY_FAILURE" : + "REAUTHENTICATION_REQUEST"); + + hdcp->reauth = true; + + if (!sde_hdcp_1x_state(HDCP_STATE_INACTIVE)) + hdcp->hdcp_state = HDCP_STATE_AUTH_FAIL; + + complete_all(&hdcp->sink_r0_available); + sde_hdcp_1x_update_auth_status(hdcp); + } else if (buf & BIT(1)) { + SDE_HDCP_DEBUG("R0' AVAILABLE\n"); + hdcp->sink_r0_ready = true; + complete_all(&hdcp->sink_r0_available); + } else if ((buf & BIT(0))) { + SDE_HDCP_DEBUG("KSVs READY\n"); + + hdcp->ksv_ready = true; + } else { + SDE_HDCP_DEBUG("spurious interrupt\n"); + } + + sde_hdcp_1x_clear_cp_irq(hdcp); + return 0; + +irq_not_handled: + return -EINVAL; +} + +void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data) +{ + struct sde_hdcp_1x *hdcp = NULL; + char name[20]; + static struct sde_hdcp_ops ops = { + .isr = sde_hdcp_1x_isr, + .cp_irq = sde_hdcp_1x_cp_irq, + .reauthenticate = sde_hdcp_1x_reauthenticate, + .authenticate = sde_hdcp_1x_authenticate, + .off = sde_hdcp_1x_off + }; + + if (!init_data || !init_data->core_io || !init_data->qfprom_io || + !init_data->mutex || !init_data->notify_status || + !init_data->workq || !init_data->cb_data) { + pr_err("invalid input\n"); + goto error; + } + + if (init_data->sec_access && !init_data->hdcp_io) { + pr_err("hdcp_io required\n"); + goto error; + } + + hdcp = kzalloc(sizeof(*hdcp), GFP_KERNEL); + if (!hdcp) + goto error; + + hdcp->init_data = *init_data; + hdcp->ops = &ops; + + snprintf(name, sizeof(name), "hdcp_1x_%d", + hdcp->init_data.client_id); + + hdcp->workq = create_workqueue(name); + if (!hdcp->workq) { + pr_err("Error creating workqueue\n"); + kfree(hdcp); + goto error; + } + + sde_hdcp_1x_update_client_reg_set(hdcp); + + INIT_DELAYED_WORK(&hdcp->hdcp_auth_work, sde_hdcp_1x_auth_work); + + hdcp->hdcp_state = HDCP_STATE_INACTIVE; + init_completion(&hdcp->r0_checked); + init_completion(&hdcp->sink_r0_available); + + SDE_HDCP_DEBUG("HDCP module initialized. HDCP_STATE=%s\n", + SDE_HDCP_STATE_NAME); + + return (void *)hdcp; + +error: + return NULL; +} /* hdcp_1x_init */ + +struct sde_hdcp_ops *sde_hdcp_1x_start(void *input) +{ + return ((struct sde_hdcp_1x *)input)->ops; +} + diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h index 82d3e28918fd..7e4f24ae7de8 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/timer.h @@ -4,6 +4,7 @@ struct nvkm_alarm { struct list_head head; + struct list_head exec; u64 timestamp; void (*func)(struct nvkm_alarm *); }; diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 58a3f7cf2fb3..00de1bf81519 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -370,7 +370,8 @@ nouveau_display_init(struct drm_device *dev) return ret; /* enable polling for external displays */ - drm_kms_helper_poll_enable(dev); + if (!dev->mode_config.poll_enabled) + drm_kms_helper_poll_enable(dev); /* enable hotplug interrupts */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index d236fc7c425b..91a61d2cca88 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -743,7 +743,10 @@ nouveau_pmops_runtime_resume(struct device *dev) pci_set_master(pdev); ret = nouveau_do_resume(drm_dev, true); - drm_kms_helper_poll_enable(drm_dev); + + if (!drm_dev->mode_config.poll_enabled) + drm_kms_helper_poll_enable(drm_dev); + /* do magic */ nvif_mask(&device->object, 0x088488, (1 << 25), (1 << 25)); vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON); diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.h b/drivers/gpu/drm/nouveau/nouveau_fence.h index 2e3a62d38fe9..1621c8ae0fa0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.h +++ b/drivers/gpu/drm/nouveau/nouveau_fence.h @@ -99,6 +99,7 @@ struct nv84_fence_priv { struct nouveau_bo *bo; struct nouveau_bo *bo_gart; u32 *suspend; + struct mutex mutex; }; u64 nv84_fence_crtc(struct nouveau_channel *, int); diff --git a/drivers/gpu/drm/nouveau/nouveau_usif.c b/drivers/gpu/drm/nouveau/nouveau_usif.c index 6ae1b3494bcd..b7b961233949 100644 --- a/drivers/gpu/drm/nouveau/nouveau_usif.c +++ b/drivers/gpu/drm/nouveau/nouveau_usif.c @@ -313,7 +313,8 @@ usif_ioctl(struct drm_file *filp, void __user *user, u32 argc) if (nvif_unpack(argv->v0, 0, 0, true)) { /* block access to objects not created via this interface */ owner = argv->v0.owner; - if (argv->v0.object == 0ULL) + if (argv->v0.object == 0ULL && + argv->v0.type != NVIF_IOCTL_V0_DEL) argv->v0.owner = NVDRM_OBJECT_ANY; /* except client */ else argv->v0.owner = NVDRM_OBJECT_USIF; diff --git a/drivers/gpu/drm/nouveau/nv84_fence.c b/drivers/gpu/drm/nouveau/nv84_fence.c index 412c5be5a9ca..7bc26eceda66 100644 --- a/drivers/gpu/drm/nouveau/nv84_fence.c +++ b/drivers/gpu/drm/nouveau/nv84_fence.c @@ -121,8 +121,10 @@ nv84_fence_context_del(struct nouveau_channel *chan) } nouveau_bo_wr32(priv->bo, chan->chid * 16 / 4, fctx->base.sequence); + mutex_lock(&priv->mutex); nouveau_bo_vma_del(priv->bo, &fctx->vma_gart); nouveau_bo_vma_del(priv->bo, &fctx->vma); + mutex_unlock(&priv->mutex); nouveau_fence_context_del(&fctx->base); chan->fence = NULL; nouveau_fence_context_free(&fctx->base); @@ -148,11 +150,13 @@ nv84_fence_context_new(struct nouveau_channel *chan) fctx->base.sync32 = nv84_fence_sync32; fctx->base.sequence = nv84_fence_read(chan); + mutex_lock(&priv->mutex); ret = nouveau_bo_vma_add(priv->bo, cli->vm, &fctx->vma); if (ret == 0) { ret = nouveau_bo_vma_add(priv->bo_gart, cli->vm, &fctx->vma_gart); } + mutex_unlock(&priv->mutex); /* map display semaphore buffers into channel's vm */ for (i = 0; !ret && i < chan->drm->dev->mode_config.num_crtc; i++) { @@ -232,6 +236,8 @@ nv84_fence_create(struct nouveau_drm *drm) priv->base.context_base = fence_context_alloc(priv->base.contexts); priv->base.uevent = true; + mutex_init(&priv->mutex); + /* Use VRAM if there is any ; otherwise fallback to system memory */ domain = drm->device.info.ram_size != 0 ? TTM_PL_FLAG_VRAM : /* diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c index 949dc6101a58..7c0b58613747 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c @@ -130,7 +130,7 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode) poll = false; } - if (list_empty(&therm->alarm.head) && poll) + if (poll) nvkm_timer_alarm(tmr, 1000000000ULL, &therm->alarm); spin_unlock_irqrestore(&therm->lock, flags); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c index 91198d79393a..e2feccec25f5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c @@ -83,7 +83,7 @@ nvkm_fan_update(struct nvkm_fan *fan, bool immediate, int target) spin_unlock_irqrestore(&fan->lock, flags); /* schedule next fan update, if not at target speed already */ - if (list_empty(&fan->alarm.head) && target != duty) { + if (target != duty) { u16 bump_period = fan->bios.bump_period; u16 slow_down_period = fan->bios.slow_down_period; u64 delay; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c index 59701b7a6597..ff9fbe7950e5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/fantog.c @@ -53,7 +53,7 @@ nvkm_fantog_update(struct nvkm_fantog *fan, int percent) duty = !nvkm_gpio_get(gpio, 0, DCB_GPIO_FAN, 0xff); nvkm_gpio_set(gpio, 0, DCB_GPIO_FAN, 0xff, duty); - if (list_empty(&fan->alarm.head) && percent != (duty * 100)) { + if (percent != (duty * 100)) { u64 next_change = (percent * fan->period_us) / 100; if (!duty) next_change = fan->period_us - next_change; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c index b9703c02d8ca..9a79e91fdfdc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c @@ -185,7 +185,7 @@ alarm_timer_callback(struct nvkm_alarm *alarm) spin_unlock_irqrestore(&therm->sensor.alarm_program_lock, flags); /* schedule the next poll in one second */ - if (therm->func->temp_get(therm) >= 0 && list_empty(&alarm->head)) + if (therm->func->temp_get(therm) >= 0) nvkm_timer_alarm(tmr, 1000000000ULL, alarm); } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c index d4dae1f12d62..46033909d950 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/base.c @@ -36,25 +36,32 @@ nvkm_timer_alarm_trigger(struct nvkm_timer *tmr) unsigned long flags; LIST_HEAD(exec); - /* move any due alarms off the pending list */ + /* Process pending alarms. */ spin_lock_irqsave(&tmr->lock, flags); list_for_each_entry_safe(alarm, atemp, &tmr->alarms, head) { - if (alarm->timestamp <= nvkm_timer_read(tmr)) - list_move_tail(&alarm->head, &exec); + /* Have we hit the earliest alarm that hasn't gone off? */ + if (alarm->timestamp > nvkm_timer_read(tmr)) { + /* Schedule it. If we didn't race, we're done. */ + tmr->func->alarm_init(tmr, alarm->timestamp); + if (alarm->timestamp > nvkm_timer_read(tmr)) + break; + } + + /* Move to completed list. We'll drop the lock before + * executing the callback so it can reschedule itself. + */ + list_del_init(&alarm->head); + list_add(&alarm->exec, &exec); } - /* reschedule interrupt for next alarm time */ - if (!list_empty(&tmr->alarms)) { - alarm = list_first_entry(&tmr->alarms, typeof(*alarm), head); - tmr->func->alarm_init(tmr, alarm->timestamp); - } else { + /* Shut down interrupt if no more pending alarms. */ + if (list_empty(&tmr->alarms)) tmr->func->alarm_fini(tmr); - } spin_unlock_irqrestore(&tmr->lock, flags); - /* execute any pending alarm handlers */ - list_for_each_entry_safe(alarm, atemp, &exec, head) { - list_del_init(&alarm->head); + /* Execute completed callbacks. */ + list_for_each_entry_safe(alarm, atemp, &exec, exec) { + list_del(&alarm->exec); alarm->func(alarm); } } @@ -65,24 +72,37 @@ nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm) struct nvkm_alarm *list; unsigned long flags; - alarm->timestamp = nvkm_timer_read(tmr) + nsec; - - /* append new alarm to list, in soonest-alarm-first order */ + /* Remove alarm from pending list. + * + * This both protects against the corruption of the list, + * and implements alarm rescheduling/cancellation. + */ spin_lock_irqsave(&tmr->lock, flags); - if (!nsec) { - if (!list_empty(&alarm->head)) - list_del(&alarm->head); - } else { + list_del_init(&alarm->head); + + if (nsec) { + /* Insert into pending list, ordered earliest to latest. */ + alarm->timestamp = nvkm_timer_read(tmr) + nsec; list_for_each_entry(list, &tmr->alarms, head) { if (list->timestamp > alarm->timestamp) break; } + list_add_tail(&alarm->head, &list->head); + + /* Update HW if this is now the earliest alarm. */ + list = list_first_entry(&tmr->alarms, typeof(*list), head); + if (list == alarm) { + tmr->func->alarm_init(tmr, alarm->timestamp); + /* This shouldn't happen if callers aren't stupid. + * + * Worst case scenario is that it'll take roughly + * 4 seconds for the next alarm to trigger. + */ + WARN_ON(alarm->timestamp <= nvkm_timer_read(tmr)); + } } spin_unlock_irqrestore(&tmr->lock, flags); - - /* process pending alarms */ - nvkm_timer_alarm_trigger(tmr); } void diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c index 7b9ce87f0617..7f48249f41de 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/timer/nv04.c @@ -76,8 +76,8 @@ nv04_timer_intr(struct nvkm_timer *tmr) u32 stat = nvkm_rd32(device, NV04_PTIMER_INTR_0); if (stat & 0x00000001) { - nvkm_timer_alarm_trigger(tmr); nvkm_wr32(device, NV04_PTIMER_INTR_0, 0x00000001); + nvkm_timer_alarm_trigger(tmr); stat &= ~0x00000001; } diff --git a/drivers/gpu/drm/radeon/ci_dpm.c b/drivers/gpu/drm/radeon/ci_dpm.c index 4a09947be244..3c32f095a873 100644 --- a/drivers/gpu/drm/radeon/ci_dpm.c +++ b/drivers/gpu/drm/radeon/ci_dpm.c @@ -776,6 +776,12 @@ bool ci_dpm_vblank_too_short(struct radeon_device *rdev) u32 vblank_time = r600_dpm_get_vblank_time(rdev); u32 switch_limit = pi->mem_gddr5 ? 450 : 300; + /* disable mclk switching if the refresh is >120Hz, even if the + * blanking period would allow it + */ + if (r600_dpm_get_vrefresh(rdev) > 120) + return true; + if (vblank_time < switch_limit) return true; else diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index f81fb2641097..134874cab4c7 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -7762,7 +7762,7 @@ static inline void cik_irq_ack(struct radeon_device *rdev) WREG32(DC_HPD5_INT_CONTROL, tmp); } if (rdev->irq.stat_regs.cik.disp_int_cont5 & DC_HPD6_INTERRUPT) { - tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp = RREG32(DC_HPD6_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; WREG32(DC_HPD6_INT_CONTROL, tmp); } @@ -7792,7 +7792,7 @@ static inline void cik_irq_ack(struct radeon_device *rdev) WREG32(DC_HPD5_INT_CONTROL, tmp); } if (rdev->irq.stat_regs.cik.disp_int_cont5 & DC_HPD6_RX_INTERRUPT) { - tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp = RREG32(DC_HPD6_INT_CONTROL); tmp |= DC_HPDx_RX_INT_ACK; WREG32(DC_HPD6_INT_CONTROL, tmp); } diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 32491355a1d4..ba9e6ed4ae54 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -4924,7 +4924,7 @@ static void evergreen_irq_ack(struct radeon_device *rdev) WREG32(DC_HPD5_INT_CONTROL, tmp); } if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_INTERRUPT) { - tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp = RREG32(DC_HPD6_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; WREG32(DC_HPD6_INT_CONTROL, tmp); } @@ -4955,7 +4955,7 @@ static void evergreen_irq_ack(struct radeon_device *rdev) WREG32(DC_HPD5_INT_CONTROL, tmp); } if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_RX_INTERRUPT) { - tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp = RREG32(DC_HPD6_INT_CONTROL); tmp |= DC_HPDx_RX_INT_ACK; WREG32(DC_HPD6_INT_CONTROL, tmp); } diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index cc2fdf0be37a..0e20c08f8977 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -3945,7 +3945,7 @@ static void r600_irq_ack(struct radeon_device *rdev) WREG32(DC_HPD5_INT_CONTROL, tmp); } if (rdev->irq.stat_regs.r600.disp_int_cont2 & DC_HPD6_INTERRUPT) { - tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp = RREG32(DC_HPD6_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; WREG32(DC_HPD6_INT_CONTROL, tmp); } diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index f878d6962da5..5cf3a2cbc07e 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -6335,7 +6335,7 @@ static inline void si_irq_ack(struct radeon_device *rdev) WREG32(DC_HPD5_INT_CONTROL, tmp); } if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_INTERRUPT) { - tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp = RREG32(DC_HPD6_INT_CONTROL); tmp |= DC_HPDx_INT_ACK; WREG32(DC_HPD6_INT_CONTROL, tmp); } @@ -6366,7 +6366,7 @@ static inline void si_irq_ack(struct radeon_device *rdev) WREG32(DC_HPD5_INT_CONTROL, tmp); } if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_RX_INTERRUPT) { - tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp = RREG32(DC_HPD6_INT_CONTROL); tmp |= DC_HPDx_RX_INT_ACK; WREG32(DC_HPD6_INT_CONTROL, tmp); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c index b6a0806b06bf..a1c68e6a689e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c @@ -368,6 +368,8 @@ static void *vmw_local_fifo_reserve(struct vmw_private *dev_priv, return fifo_state->static_buffer; else { fifo_state->dynamic_buffer = vmalloc(bytes); + if (!fifo_state->dynamic_buffer) + goto out_err; return fifo_state->dynamic_buffer; } } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c index c9c04ccccdd9..027987023400 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -1288,11 +1288,14 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data, struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; int ret; uint32_t size; - uint32_t backup_handle; + uint32_t backup_handle = 0; if (req->multisample_count != 0) return -EINVAL; + if (req->mip_levels > DRM_VMW_MAX_MIP_LEVELS) + return -EINVAL; + if (unlikely(vmw_user_surface_size == 0)) vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) + 128; @@ -1328,12 +1331,16 @@ int vmw_gb_surface_define_ioctl(struct drm_device *dev, void *data, ret = vmw_user_dmabuf_lookup(tfile, req->buffer_handle, &res->backup, &user_srf->backup_base); - if (ret == 0 && res->backup->base.num_pages * PAGE_SIZE < - res->backup_size) { - DRM_ERROR("Surface backup buffer is too small.\n"); - vmw_dmabuf_unreference(&res->backup); - ret = -EINVAL; - goto out_unlock; + if (ret == 0) { + if (res->backup->base.num_pages * PAGE_SIZE < + res->backup_size) { + DRM_ERROR("Surface backup buffer is too small.\n"); + vmw_dmabuf_unreference(&res->backup); + ret = -EINVAL; + goto out_unlock; + } else { + backup_handle = req->buffer_handle; + } } } else if (req->drm_surface_flags & drm_vmw_surface_flag_create_buffer) ret = vmw_user_dmabuf_alloc(dev_priv, tfile, diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 89c7590ad121..6521ec01413e 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -2139,6 +2139,11 @@ static int adreno_soft_reset(struct kgsl_device *device) /* Reset the GPU */ _soft_reset(adreno_dev); + /* Clear the busy_data stats - we're starting over from scratch */ + adreno_dev->busy_data.gpu_busy = 0; + adreno_dev->busy_data.vbif_ram_cycles = 0; + adreno_dev->busy_data.vbif_starved_ram = 0; + /* Set the page table back to the default page table */ adreno_ringbuffer_set_global(adreno_dev, 0); kgsl_mmu_set_pt(&device->mmu, device->mmu.defaultpagetable); diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index dcc6651710fe..0715022be6e3 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.c @@ -59,7 +59,7 @@ static const struct adreno_vbif_platform a5xx_vbif_platforms[] = { { adreno_is_a530, a530_vbif }, { adreno_is_a512, a540_vbif }, { adreno_is_a510, a530_vbif }, - { adreno_is_a508, a540_vbif }, + { adreno_is_a508, a530_vbif }, { adreno_is_a505, a530_vbif }, { adreno_is_a506, a530_vbif }, }; diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c index 55f906c9cb90..8902c3175c79 100644 --- a/drivers/gpu/msm/adreno_dispatch.c +++ b/drivers/gpu/msm/adreno_dispatch.c @@ -979,6 +979,13 @@ static void _adreno_dispatcher_issuecmds(struct adreno_device *adreno_dev) spin_unlock(&dispatcher->plist_lock); } +static inline void _decrement_submit_now(struct kgsl_device *device) +{ + spin_lock(&device->submit_lock); + device->submit_now--; + spin_unlock(&device->submit_lock); +} + /** * adreno_dispatcher_issuecmds() - Issue commmands from pending contexts * @adreno_dev: Pointer to the adreno device struct @@ -988,15 +995,29 @@ static void _adreno_dispatcher_issuecmds(struct adreno_device *adreno_dev) static void adreno_dispatcher_issuecmds(struct adreno_device *adreno_dev) { struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; + struct kgsl_device *device = KGSL_DEVICE(adreno_dev); + + spin_lock(&device->submit_lock); + /* If state transition to SLUMBER, schedule the work for later */ + if (device->slumber == true) { + spin_unlock(&device->submit_lock); + goto done; + } + device->submit_now++; + spin_unlock(&device->submit_lock); /* If the dispatcher is busy then schedule the work for later */ if (!mutex_trylock(&dispatcher->mutex)) { - adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev)); - return; + _decrement_submit_now(device); + goto done; } _adreno_dispatcher_issuecmds(adreno_dev); mutex_unlock(&dispatcher->mutex); + _decrement_submit_now(device); + return; +done: + adreno_dispatcher_schedule(device); } /** @@ -2422,7 +2443,7 @@ static void _dispatcher_power_down(struct adreno_device *adreno_dev) mutex_unlock(&device->mutex); } -static void adreno_dispatcher_work(struct work_struct *work) +static void adreno_dispatcher_work(struct kthread_work *work) { struct adreno_dispatcher *dispatcher = container_of(work, struct adreno_dispatcher, work); @@ -2482,7 +2503,7 @@ void adreno_dispatcher_schedule(struct kgsl_device *device) struct adreno_device *adreno_dev = ADRENO_DEVICE(device); struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; - kgsl_schedule_work(&dispatcher->work); + queue_kthread_work(&kgsl_driver.worker, &dispatcher->work); } /** @@ -2778,7 +2799,7 @@ int adreno_dispatcher_init(struct adreno_device *adreno_dev) setup_timer(&dispatcher->fault_timer, adreno_dispatcher_fault_timer, (unsigned long) adreno_dev); - INIT_WORK(&dispatcher->work, adreno_dispatcher_work); + init_kthread_work(&dispatcher->work, adreno_dispatcher_work); init_completion(&dispatcher->idle_gate); complete_all(&dispatcher->idle_gate); diff --git a/drivers/gpu/msm/adreno_dispatch.h b/drivers/gpu/msm/adreno_dispatch.h index 72545db12f90..48f0cdc546ff 100644 --- a/drivers/gpu/msm/adreno_dispatch.h +++ b/drivers/gpu/msm/adreno_dispatch.h @@ -91,7 +91,7 @@ struct adreno_dispatcher { atomic_t fault; struct plist_head pending; spinlock_t plist_lock; - struct work_struct work; + struct kthread_work work; struct kobject kobj; struct completion idle_gate; unsigned int disp_preempt_fair_sched; diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index b2def8dea954..990c9bca5127 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.c @@ -255,13 +255,6 @@ static void _deferred_put(struct work_struct *work) kgsl_mem_entry_put(entry); } -static inline void -kgsl_mem_entry_put_deferred(struct kgsl_mem_entry *entry) -{ - if (entry) - queue_work(kgsl_driver.mem_workqueue, &entry->work); -} - static inline struct kgsl_mem_entry * kgsl_mem_entry_create(void) { @@ -272,7 +265,6 @@ kgsl_mem_entry_create(void) /* put this ref in the caller functions after init */ kref_get(&entry->refcount); - INIT_WORK(&entry->work, _deferred_put); } return entry; } @@ -1869,7 +1861,7 @@ long kgsl_ioctl_sharedmem_free(struct kgsl_device_private *dev_priv, return -EINVAL; ret = gpumem_free_entry(entry); - kgsl_mem_entry_put_deferred(entry); + kgsl_mem_entry_put(entry); return ret; } @@ -1887,7 +1879,7 @@ long kgsl_ioctl_gpumem_free_id(struct kgsl_device_private *dev_priv, return -EINVAL; ret = gpumem_free_entry(entry); - kgsl_mem_entry_put_deferred(entry); + kgsl_mem_entry_put(entry); return ret; } @@ -1924,7 +1916,8 @@ static void gpuobj_free_fence_func(void *priv) { struct kgsl_mem_entry *entry = priv; - kgsl_mem_entry_put_deferred(entry); + INIT_WORK(&entry->work, _deferred_put); + queue_work(kgsl_driver.mem_workqueue, &entry->work); } static long gpuobj_free_on_fence(struct kgsl_device_private *dev_priv, @@ -1988,7 +1981,7 @@ long kgsl_ioctl_gpuobj_free(struct kgsl_device_private *dev_priv, else ret = -EINVAL; - kgsl_mem_entry_put_deferred(entry); + kgsl_mem_entry_put(entry); return ret; } @@ -3363,13 +3356,7 @@ long kgsl_ioctl_sparse_phys_free(struct kgsl_device_private *dev_priv, if (entry == NULL) return -EINVAL; - if (!kgsl_mem_entry_set_pend(entry)) { - kgsl_mem_entry_put(entry); - return -EBUSY; - } - if (entry->memdesc.cur_bindings != 0) { - kgsl_mem_entry_unset_pend(entry); kgsl_mem_entry_put(entry); return -EINVAL; } @@ -3378,7 +3365,7 @@ long kgsl_ioctl_sparse_phys_free(struct kgsl_device_private *dev_priv, /* One put for find_id(), one put for the kgsl_mem_entry_create() */ kgsl_mem_entry_put(entry); - kgsl_mem_entry_put_deferred(entry); + kgsl_mem_entry_put(entry); return 0; } @@ -3438,13 +3425,7 @@ long kgsl_ioctl_sparse_virt_free(struct kgsl_device_private *dev_priv, if (entry == NULL) return -EINVAL; - if (!kgsl_mem_entry_set_pend(entry)) { - kgsl_mem_entry_put(entry); - return -EBUSY; - } - if (entry->bind_tree.rb_node != NULL) { - kgsl_mem_entry_unset_pend(entry); kgsl_mem_entry_put(entry); return -EINVAL; } @@ -3453,7 +3434,7 @@ long kgsl_ioctl_sparse_virt_free(struct kgsl_device_private *dev_priv, /* One put for find_id(), one put for the kgsl_mem_entry_create() */ kgsl_mem_entry_put(entry); - kgsl_mem_entry_put_deferred(entry); + kgsl_mem_entry_put(entry); return 0; } @@ -4719,6 +4700,7 @@ int kgsl_device_platform_probe(struct kgsl_device *device) device->id, device->reg_phys, device->reg_len); rwlock_init(&device->context_lock); + spin_lock_init(&device->submit_lock); setup_timer(&device->idle_timer, kgsl_timer, (unsigned long) device); @@ -4863,6 +4845,8 @@ static void kgsl_core_exit(void) static int __init kgsl_core_init(void) { int result = 0; + struct sched_param param = { .sched_priority = 2 }; + /* alloc major and minor device numbers */ result = alloc_chrdev_region(&kgsl_driver.major, 0, KGSL_DEVICE_MAX, "kgsl"); @@ -4926,7 +4910,19 @@ static int __init kgsl_core_init(void) WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, 0); kgsl_driver.mem_workqueue = alloc_workqueue("kgsl-mementry", - WQ_MEM_RECLAIM, 0); + WQ_UNBOUND | WQ_MEM_RECLAIM, 0); + + init_kthread_worker(&kgsl_driver.worker); + + kgsl_driver.worker_thread = kthread_run(kthread_worker_fn, + &kgsl_driver.worker, "kgsl_worker_thread"); + + if (IS_ERR(kgsl_driver.worker_thread)) { + pr_err("unable to start kgsl thread\n"); + goto err; + } + + sched_setscheduler(kgsl_driver.worker_thread, SCHED_FIFO, ¶m); kgsl_events_init(); diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index 2a9ac899725c..faf38d1d2293 100644 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -26,6 +26,7 @@ #include <linux/mm.h> #include <linux/dma-attrs.h> #include <linux/uaccess.h> +#include <linux/kthread.h> #include <asm/cacheflush.h> /* @@ -152,6 +153,8 @@ struct kgsl_driver { unsigned int full_cache_threshold; struct workqueue_struct *workqueue; struct workqueue_struct *mem_workqueue; + struct kthread_worker worker; + struct task_struct *worker_thread; }; extern struct kgsl_driver kgsl_driver; @@ -301,7 +304,7 @@ struct kgsl_event { void *priv; struct list_head node; unsigned int created; - struct work_struct work; + struct kthread_work work; int result; struct kgsl_event_group *group; }; diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h index d93fd9bfbcd0..64dd45a30612 100644 --- a/drivers/gpu/msm/kgsl_device.h +++ b/drivers/gpu/msm/kgsl_device.h @@ -256,6 +256,11 @@ struct kgsl_device { struct kgsl_pwrctrl pwrctrl; int open_count; + /* For GPU inline submission */ + uint32_t submit_now; + spinlock_t submit_lock; + bool slumber; + struct mutex mutex; uint32_t state; uint32_t requested_state; diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c index 6e8abf36c50f..859511baba12 100644 --- a/drivers/gpu/msm/kgsl_events.c +++ b/drivers/gpu/msm/kgsl_events.c @@ -32,7 +32,7 @@ static inline void signal_event(struct kgsl_device *device, { list_del(&event->node); event->result = result; - queue_work(device->events_wq, &event->work); + queue_kthread_work(&kgsl_driver.worker, &event->work); } /** @@ -42,7 +42,7 @@ static inline void signal_event(struct kgsl_device *device, * Each event callback has its own work struct and is run on a event specific * workqeuue. This is the worker that queues up the event callback function. */ -static void _kgsl_event_worker(struct work_struct *work) +static void _kgsl_event_worker(struct kthread_work *work) { struct kgsl_event *event = container_of(work, struct kgsl_event, work); int id = KGSL_CONTEXT_ID(event->context); @@ -282,7 +282,7 @@ int kgsl_add_event(struct kgsl_device *device, struct kgsl_event_group *group, event->created = jiffies; event->group = group; - INIT_WORK(&event->work, _kgsl_event_worker); + init_kthread_work(&event->work, _kgsl_event_worker); trace_kgsl_register_event(KGSL_CONTEXT_ID(context), timestamp, func); @@ -297,7 +297,7 @@ int kgsl_add_event(struct kgsl_device *device, struct kgsl_event_group *group, if (timestamp_cmp(retired, timestamp) >= 0) { event->result = KGSL_EVENT_RETIRED; - queue_work(device->events_wq, &event->work); + queue_kthread_work(&kgsl_driver.worker, &event->work); spin_unlock(&group->lock); return 0; } diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c index bb92b8b79d93..c31a85b07447 100644 --- a/drivers/gpu/msm/kgsl_pool.c +++ b/drivers/gpu/msm/kgsl_pool.c @@ -280,6 +280,17 @@ static int kgsl_pool_idx_lookup(unsigned int order) return -ENOMEM; } +static int kgsl_pool_get_retry_order(unsigned int order) +{ + int i; + + for (i = kgsl_num_pools-1; i > 0; i--) + if (order >= kgsl_pools[i].pool_order) + return kgsl_pools[i].pool_order; + + return 0; +} + /** * kgsl_pool_alloc_page() - Allocate a page of requested size * @page_size: Size of the page to be allocated @@ -326,7 +337,7 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages, if (pool == NULL) { /* Retry with lower order pages */ if (order > 0) { - size = PAGE_SIZE << --order; + size = PAGE_SIZE << kgsl_pool_get_retry_order(order); goto eagain; } else { /* diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 0150d50c925b..e42f92392e8d 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -81,6 +81,12 @@ static void kgsl_pwrctrl_set_state(struct kgsl_device *device, static void kgsl_pwrctrl_request_state(struct kgsl_device *device, unsigned int state); static int _isense_clk_set_rate(struct kgsl_pwrctrl *pwr, int level); +static int kgsl_pwrctrl_clk_set_rate(struct clk *grp_clk, unsigned int freq, + const char *name); +static void _gpu_clk_prepare_enable(struct kgsl_device *device, + struct clk *clk, const char *name); +static void _bimc_clk_prepare_enable(struct kgsl_device *device, + struct clk *clk, const char *name); /** * _record_pwrevent() - Record the history of the new event @@ -405,7 +411,8 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, pwrlevel = &pwr->pwrlevels[pwr->active_pwrlevel]; /* Change register settings if any BEFORE pwrlevel change*/ kgsl_pwrctrl_pwrlevel_change_settings(device, 0); - clk_set_rate(pwr->grp_clks[0], pwrlevel->gpu_freq); + kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0], + pwrlevel->gpu_freq, clocks[0]); _isense_clk_set_rate(pwr, pwr->active_pwrlevel); trace_kgsl_pwrlevel(device, @@ -423,9 +430,12 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, if (pwr->gpu_bimc_int_clk) { if (pwr->active_pwrlevel == 0 && !pwr->gpu_bimc_interface_enabled) { - clk_set_rate(pwr->gpu_bimc_int_clk, - pwr->gpu_bimc_int_clk_freq); - clk_prepare_enable(pwr->gpu_bimc_int_clk); + kgsl_pwrctrl_clk_set_rate(pwr->gpu_bimc_int_clk, + pwr->gpu_bimc_int_clk_freq, + "bimc_gpu_clk"); + _bimc_clk_prepare_enable(device, + pwr->gpu_bimc_int_clk, + "bimc_gpu_clk"); pwr->gpu_bimc_interface_enabled = 1; } else if (pwr->previous_pwrlevel == 0 && pwr->gpu_bimc_interface_enabled) { @@ -1650,9 +1660,9 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state, (requested_state != KGSL_STATE_NAP)) { for (i = KGSL_MAX_CLKS - 1; i > 0; i--) clk_unprepare(pwr->grp_clks[i]); - clk_set_rate(pwr->grp_clks[0], + kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0], pwr->pwrlevels[pwr->num_pwrlevels - 1]. - gpu_freq); + gpu_freq, clocks[0]); _isense_clk_set_rate(pwr, pwr->num_pwrlevels - 1); } @@ -1664,9 +1674,9 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state, for (i = KGSL_MAX_CLKS - 1; i > 0; i--) clk_unprepare(pwr->grp_clks[i]); if ((pwr->pwrlevels[0].gpu_freq > 0)) { - clk_set_rate(pwr->grp_clks[0], + kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0], pwr->pwrlevels[pwr->num_pwrlevels - 1]. - gpu_freq); + gpu_freq, clocks[0]); _isense_clk_set_rate(pwr, pwr->num_pwrlevels - 1); } @@ -1679,29 +1689,31 @@ static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state, /* High latency clock maintenance. */ if (device->state != KGSL_STATE_NAP) { if (pwr->pwrlevels[0].gpu_freq > 0) { - clk_set_rate(pwr->grp_clks[0], + kgsl_pwrctrl_clk_set_rate( + pwr->grp_clks[0], pwr->pwrlevels [pwr->active_pwrlevel]. - gpu_freq); + gpu_freq, clocks[0]); _isense_clk_set_rate(pwr, pwr->active_pwrlevel); } - - for (i = KGSL_MAX_CLKS - 1; i > 0; i--) - clk_prepare(pwr->grp_clks[i]); } - /* as last step, enable grp_clk - this is to let GPU interrupt to come */ + for (i = KGSL_MAX_CLKS - 1; i > 0; i--) - clk_enable(pwr->grp_clks[i]); + _gpu_clk_prepare_enable(device, + pwr->grp_clks[i], clocks[i]); + /* Enable the gpu-bimc-interface clocks */ if (pwr->gpu_bimc_int_clk) { if (pwr->active_pwrlevel == 0 && !pwr->gpu_bimc_interface_enabled) { - clk_set_rate(pwr->gpu_bimc_int_clk, - pwr->gpu_bimc_int_clk_freq); - clk_prepare_enable( - pwr->gpu_bimc_int_clk); + kgsl_pwrctrl_clk_set_rate( + pwr->gpu_bimc_int_clk, + pwr->gpu_bimc_int_clk_freq, + "bimc_gpu_clk"); + _bimc_clk_prepare_enable(device, + pwr->gpu_bimc_int_clk, + "bimc_gpu_clk"); pwr->gpu_bimc_interface_enabled = 1; } } @@ -2022,7 +2034,54 @@ static int _isense_clk_set_rate(struct kgsl_pwrctrl *pwr, int level) rate = clk_round_rate(pwr->grp_clks[pwr->isense_clk_indx], level > pwr->isense_clk_on_level ? KGSL_XO_CLK_FREQ : KGSL_ISENSE_CLK_FREQ); - return clk_set_rate(pwr->grp_clks[pwr->isense_clk_indx], rate); + return kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[pwr->isense_clk_indx], + rate, clocks[pwr->isense_clk_indx]); +} + +/* + * _gpu_clk_prepare_enable - Enable the specified GPU clock + * Try once to enable it and then BUG() for debug + */ +static void _gpu_clk_prepare_enable(struct kgsl_device *device, + struct clk *clk, const char *name) +{ + int ret; + + if (device->state == KGSL_STATE_NAP) { + ret = clk_enable(clk); + if (ret) + goto err; + return; + } + + ret = clk_prepare_enable(clk); + if (!ret) + return; +err: + /* Failure is fatal so BUG() to facilitate debug */ + KGSL_DRV_FATAL(device, "KGSL:%s enable error:%d\n", name, ret); +} + +/* + * _bimc_clk_prepare_enable - Enable the specified GPU clock + * Try once to enable it and then BUG() for debug + */ +static void _bimc_clk_prepare_enable(struct kgsl_device *device, + struct clk *clk, const char *name) +{ + int ret = clk_prepare_enable(clk); + /* Failure is fatal so BUG() to facilitate debug */ + if (ret) + KGSL_DRV_FATAL(device, "KGSL:%s enable error:%d\n", name, ret); +} + +static int kgsl_pwrctrl_clk_set_rate(struct clk *grp_clk, unsigned int freq, + const char *name) +{ + int ret = clk_set_rate(grp_clk, freq); + + WARN(ret, "KGSL:%s set freq %d failed:%d\n", name, freq, ret); + return ret; } static inline void _close_pcl(struct kgsl_pwrctrl *pwr) @@ -2117,11 +2176,12 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) pwr->pwrlevels[i].gpu_freq = freq; } - clk_set_rate(pwr->grp_clks[0], - pwr->pwrlevels[pwr->num_pwrlevels - 1].gpu_freq); + kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[0], + pwr->pwrlevels[pwr->num_pwrlevels - 1].gpu_freq, clocks[0]); - clk_set_rate(pwr->grp_clks[6], - clk_round_rate(pwr->grp_clks[6], KGSL_RBBMTIMER_CLK_FREQ)); + kgsl_pwrctrl_clk_set_rate(pwr->grp_clks[6], + clk_round_rate(pwr->grp_clks[6], KGSL_RBBMTIMER_CLK_FREQ), + clocks[6]); _isense_clk_set_rate(pwr, pwr->num_pwrlevels - 1); @@ -2347,9 +2407,24 @@ void kgsl_idle_check(struct work_struct *work) || device->state == KGSL_STATE_NAP) { if (!atomic_read(&device->active_cnt)) { + spin_lock(&device->submit_lock); + if (device->submit_now) { + spin_unlock(&device->submit_lock); + goto done; + } + /* Don't allow GPU inline submission in SLUMBER */ + if (requested_state == KGSL_STATE_SLUMBER) + device->slumber = true; + spin_unlock(&device->submit_lock); + ret = kgsl_pwrctrl_change_state(device, device->requested_state); if (ret == -EBUSY) { + if (requested_state == KGSL_STATE_SLUMBER) { + spin_lock(&device->submit_lock); + device->slumber = false; + spin_unlock(&device->submit_lock); + } /* * If the GPU is currently busy, restore * the requested state and reschedule @@ -2360,7 +2435,7 @@ void kgsl_idle_check(struct work_struct *work) kgsl_schedule_work(&device->idle_check_ws); } } - +done: if (!ret) kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); @@ -2789,6 +2864,13 @@ static void kgsl_pwrctrl_set_state(struct kgsl_device *device, trace_kgsl_pwr_set_state(device, state); device->state = state; device->requested_state = KGSL_STATE_NONE; + + spin_lock(&device->submit_lock); + if (state == KGSL_STATE_SLUMBER || state == KGSL_STATE_SUSPEND) + device->slumber = true; + else + device->slumber = false; + spin_unlock(&device->submit_lock); } static void kgsl_pwrctrl_request_state(struct kgsl_device *device, diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index fa95b4dfe718..27733b068434 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -20,6 +20,7 @@ #include <linux/scatterlist.h> #include <soc/qcom/scm.h> #include <soc/qcom/secure_buffer.h> +#include <linux/ratelimit.h> #include "kgsl.h" #include "kgsl_sharedmem.h" @@ -623,6 +624,9 @@ int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset, uint64_t size, unsigned int op) { void *addr = NULL; + struct sg_table *sgt = NULL; + struct scatterlist *sg; + unsigned int i, pos = 0; int ret = 0; if (size == 0 || size > UINT_MAX) @@ -650,40 +654,38 @@ int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset, * If the buffer is not to mapped to kernel, perform cache * operations after mapping to kernel. */ - if (memdesc->sgt != NULL) { - struct scatterlist *sg; - unsigned int i, pos = 0; + if (memdesc->sgt != NULL) + sgt = memdesc->sgt; + else { + if (memdesc->pages == NULL) + return ret; + + sgt = kgsl_alloc_sgt_from_pages(memdesc); + if (IS_ERR(sgt)) + return PTR_ERR(sgt); + } - for_each_sg(memdesc->sgt->sgl, sg, memdesc->sgt->nents, i) { - uint64_t sg_offset, sg_left; + for_each_sg(sgt->sgl, sg, sgt->nents, i) { + uint64_t sg_offset, sg_left; - if (offset >= (pos + sg->length)) { - pos += sg->length; - continue; - } - sg_offset = offset > pos ? offset - pos : 0; - sg_left = (sg->length - sg_offset > size) ? size : - sg->length - sg_offset; - ret = kgsl_do_cache_op(sg_page(sg), NULL, sg_offset, - sg_left, op); - size -= sg_left; - if (size == 0) - break; + if (offset >= (pos + sg->length)) { pos += sg->length; + continue; } - } else if (memdesc->pages != NULL) { - addr = vmap(memdesc->pages, memdesc->page_count, - VM_IOREMAP, pgprot_writecombine(PAGE_KERNEL)); - if (addr == NULL) - return -ENOMEM; + sg_offset = offset > pos ? offset - pos : 0; + sg_left = (sg->length - sg_offset > size) ? size : + sg->length - sg_offset; + ret = kgsl_do_cache_op(sg_page(sg), NULL, sg_offset, + sg_left, op); + size -= sg_left; + if (size == 0) + break; + pos += sg->length; + } - /* Make sure the offset + size do not overflow the address */ - if (addr + ((size_t) offset + (size_t) size) < addr) - return -ERANGE; + if (memdesc->sgt == NULL) + kgsl_free_sgt(sgt); - ret = kgsl_do_cache_op(NULL, addr, offset, size, op); - vunmap(addr); - } return ret; } EXPORT_SYMBOL(kgsl_cache_range_op); @@ -698,6 +700,10 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, size_t len; unsigned int align; + static DEFINE_RATELIMIT_STATE(_rs, + DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + size = PAGE_ALIGN(size); if (size == 0 || size > UINT_MAX) return -EINVAL; @@ -760,7 +766,8 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc, */ memdesc->size = (size - len); - if (sharedmem_noretry_flag != true) + if (sharedmem_noretry_flag != true && + __ratelimit(&_rs)) KGSL_CORE_ERR( "Out of memory: only allocated %lldKB of %lldKB requested\n", (size - len) >> 10, size >> 10); diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 1a2032c2c1fb..690a9f0fa042 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -28,6 +28,8 @@ #define UHID_NAME "uhid" #define UHID_BUFSIZE 32 +static DEFINE_MUTEX(uhid_open_mutex); + struct uhid_device { struct mutex devlock; bool running; @@ -142,15 +144,26 @@ static void uhid_hid_stop(struct hid_device *hid) static int uhid_hid_open(struct hid_device *hid) { struct uhid_device *uhid = hid->driver_data; + int retval = 0; - return uhid_queue_event(uhid, UHID_OPEN); + mutex_lock(&uhid_open_mutex); + if (!hid->open++) { + retval = uhid_queue_event(uhid, UHID_OPEN); + if (retval) + hid->open--; + } + mutex_unlock(&uhid_open_mutex); + return retval; } static void uhid_hid_close(struct hid_device *hid) { struct uhid_device *uhid = hid->driver_data; - uhid_queue_event(uhid, UHID_CLOSE); + mutex_lock(&uhid_open_mutex); + if (!--hid->open) + uhid_queue_event(uhid, UHID_CLOSE); + mutex_unlock(&uhid_open_mutex); } static int uhid_hid_parse(struct hid_device *hid) diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 35e3fd9fadf6..b62c50d1b1e4 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -1440,37 +1440,38 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len) { unsigned char *data = wacom->data; - if (wacom->pen_input) + if (wacom->pen_input) { dev_dbg(wacom->pen_input->dev.parent, "%s: received report #%d\n", __func__, data[0]); - else if (wacom->touch_input) + + if (len == WACOM_PKGLEN_PENABLED || + data[0] == WACOM_REPORT_PENABLED) + return wacom_tpc_pen(wacom); + } + else if (wacom->touch_input) { dev_dbg(wacom->touch_input->dev.parent, "%s: received report #%d\n", __func__, data[0]); - switch (len) { - case WACOM_PKGLEN_TPC1FG: - return wacom_tpc_single_touch(wacom, len); + switch (len) { + case WACOM_PKGLEN_TPC1FG: + return wacom_tpc_single_touch(wacom, len); - case WACOM_PKGLEN_TPC2FG: - return wacom_tpc_mt_touch(wacom); + case WACOM_PKGLEN_TPC2FG: + return wacom_tpc_mt_touch(wacom); - case WACOM_PKGLEN_PENABLED: - return wacom_tpc_pen(wacom); + default: + switch (data[0]) { + case WACOM_REPORT_TPC1FG: + case WACOM_REPORT_TPCHID: + case WACOM_REPORT_TPCST: + case WACOM_REPORT_TPC1FGE: + return wacom_tpc_single_touch(wacom, len); - default: - switch (data[0]) { - case WACOM_REPORT_TPC1FG: - case WACOM_REPORT_TPCHID: - case WACOM_REPORT_TPCST: - case WACOM_REPORT_TPC1FGE: - return wacom_tpc_single_touch(wacom, len); - - case WACOM_REPORT_TPCMT: - case WACOM_REPORT_TPCMT2: - return wacom_mt_touch(wacom); + case WACOM_REPORT_TPCMT: + case WACOM_REPORT_TPCMT2: + return wacom_mt_touch(wacom); - case WACOM_REPORT_PENABLED: - return wacom_tpc_pen(wacom); + } } } diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c index 7f98d9f527b9..1f042fa56eea 100644 --- a/drivers/i2c/busses/i2c-msm-v2.c +++ b/drivers/i2c/busses/i2c-msm-v2.c @@ -1310,7 +1310,8 @@ static int i2c_msm_dma_xfer_process(struct i2c_msm_ctrl *ctrl) ret = i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.complete); if (!ret && ctrl->xfer.rx_cnt) - i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.rx_complete); + ret = i2c_msm_xfer_wait_for_completion(ctrl, + &ctrl->xfer.rx_complete); dma_xfer_end: /* free scatter-gather lists */ @@ -1716,9 +1717,7 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid) void __iomem *base = ctrl->rsrcs.base; struct i2c_msm_xfer *xfer = &ctrl->xfer; struct i2c_msm_xfer_mode_blk *blk = &ctrl->xfer.blk; - u32 i2c_status = 0; u32 err_flags = 0; - u32 qup_op = 0; u32 clr_flds = 0; bool log_event = false; bool signal_complete = false; @@ -1731,24 +1730,24 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid) return IRQ_HANDLED; } - i2c_status = readl_relaxed(base + QUP_I2C_STATUS); - err_flags = readl_relaxed(base + QUP_ERROR_FLAGS); - qup_op = readl_relaxed(base + QUP_OPERATIONAL); + ctrl->i2c_sts_reg = readl_relaxed(base + QUP_I2C_STATUS); + err_flags = readl_relaxed(base + QUP_ERROR_FLAGS); + ctrl->qup_op_reg = readl_relaxed(base + QUP_OPERATIONAL); - if (i2c_status & QUP_MSTR_STTS_ERR_MASK) { + if (ctrl->i2c_sts_reg & QUP_MSTR_STTS_ERR_MASK) { signal_complete = true; log_event = true; /* * If there is more than 1 error here, last one sticks. * The order of the error set here matters. */ - if (i2c_status & QUP_ARB_LOST) + if (ctrl->i2c_sts_reg & QUP_ARB_LOST) ctrl->xfer.err = I2C_MSM_ERR_ARB_LOST; - if (i2c_status & QUP_BUS_ERROR) + if (ctrl->i2c_sts_reg & QUP_BUS_ERROR) ctrl->xfer.err = I2C_MSM_ERR_BUS_ERR; - if (i2c_status & QUP_PACKET_NACKED) + if (ctrl->i2c_sts_reg & QUP_PACKET_NACKED) ctrl->xfer.err = I2C_MSM_ERR_NACK; } @@ -1761,7 +1760,7 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid) i2c_msm_dbg_qup_reg_dump(ctrl); /* clear interrupts fields */ - clr_flds = i2c_status & QUP_MSTR_STTS_ERR_MASK; + clr_flds = ctrl->i2c_sts_reg & QUP_MSTR_STTS_ERR_MASK; if (clr_flds) { writel_relaxed(clr_flds, base + QUP_I2C_STATUS); need_wmb = true; @@ -1773,7 +1772,9 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid) need_wmb = true; } - clr_flds = qup_op & (QUP_OUTPUT_SERVICE_FLAG | QUP_INPUT_SERVICE_FLAG); + clr_flds = ctrl->qup_op_reg & + (QUP_OUTPUT_SERVICE_FLAG | + QUP_INPUT_SERVICE_FLAG); if (clr_flds) { writel_relaxed(clr_flds, base + QUP_OPERATIONAL); need_wmb = true; @@ -1814,25 +1815,25 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid) /* handle data completion */ if (xfer->mode_id == I2C_MSM_XFER_MODE_BLOCK) { /* block ready for writing */ - if (qup_op & QUP_OUTPUT_SERVICE_FLAG) { + if (ctrl->qup_op_reg & QUP_OUTPUT_SERVICE_FLAG) { log_event = true; - if (qup_op & QUP_OUT_BLOCK_WRITE_REQ) + if (ctrl->qup_op_reg & QUP_OUT_BLOCK_WRITE_REQ) complete(&blk->wait_tx_blk); - if ((qup_op & blk->complete_mask) + if ((ctrl->qup_op_reg & blk->complete_mask) == blk->complete_mask) { log_event = true; signal_complete = true; } } /* block ready for reading */ - if (qup_op & QUP_INPUT_SERVICE_FLAG) { + if (ctrl->qup_op_reg & QUP_INPUT_SERVICE_FLAG) { log_event = true; complete(&blk->wait_rx_blk); } } else { /* for FIFO/DMA Mode*/ - if (qup_op & QUP_MAX_INPUT_DONE_FLAG) { + if (ctrl->qup_op_reg & QUP_MAX_INPUT_DONE_FLAG) { log_event = true; /* * If last transaction is an input then the entire @@ -1850,7 +1851,7 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid) * here QUP_OUTPUT_SERVICE_FLAG and assumes that * QUP_MAX_OUTPUT_DONE_FLAG. */ - if (qup_op & (QUP_OUTPUT_SERVICE_FLAG | + if (ctrl->qup_op_reg & (QUP_OUTPUT_SERVICE_FLAG | QUP_MAX_OUTPUT_DONE_FLAG)) { log_event = true; /* @@ -1863,13 +1864,11 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid) } isr_end: - if (ctrl->xfer.err || (ctrl->dbgfs.dbg_lvl >= MSM_DBG)) - i2c_msm_dbg_dump_diag(ctrl, true, i2c_status, qup_op); - if (log_event || (ctrl->dbgfs.dbg_lvl >= MSM_DBG)) i2c_msm_prof_evnt_add(ctrl, MSM_PROF, I2C_MSM_IRQ_END, - i2c_status, qup_op, err_flags); + ctrl->i2c_sts_reg, ctrl->qup_op_reg, + err_flags); if (signal_complete) complete(&ctrl->xfer.complete); @@ -2078,8 +2077,12 @@ static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl, xfer->timeout, time_left, 0); } else { /* return an error if one detected by ISR */ - if (xfer->err) + if (ctrl->xfer.err || + (ctrl->dbgfs.dbg_lvl >= MSM_DBG)) { + i2c_msm_dbg_dump_diag(ctrl, true, + ctrl->i2c_sts_reg, ctrl->qup_op_reg); ret = -(xfer->err); + } i2c_msm_prof_evnt_add(ctrl, MSM_DBG, I2C_MSM_COMPLT_OK, xfer->timeout, time_left, 0); } diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 630bce68bf38..b61db9db3ca5 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -54,7 +54,7 @@ #define SMBSLVDAT (0xC + piix4_smba) /* count for request_region */ -#define SMBIOSIZE 8 +#define SMBIOSIZE 9 /* PCI Address Constants */ #define SMBBA 0x090 diff --git a/drivers/i2c/busses/i2c-tiny-usb.c b/drivers/i2c/busses/i2c-tiny-usb.c index 0ed77eeff31e..a2e3dd715380 100644 --- a/drivers/i2c/busses/i2c-tiny-usb.c +++ b/drivers/i2c/busses/i2c-tiny-usb.c @@ -178,22 +178,39 @@ static int usb_read(struct i2c_adapter *adapter, int cmd, int value, int index, void *data, int len) { struct i2c_tiny_usb *dev = (struct i2c_tiny_usb *)adapter->algo_data; + void *dmadata = kmalloc(len, GFP_KERNEL); + int ret; + + if (!dmadata) + return -ENOMEM; /* do control transfer */ - return usb_control_msg(dev->usb_dev, usb_rcvctrlpipe(dev->usb_dev, 0), + ret = usb_control_msg(dev->usb_dev, usb_rcvctrlpipe(dev->usb_dev, 0), cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE | - USB_DIR_IN, value, index, data, len, 2000); + USB_DIR_IN, value, index, dmadata, len, 2000); + + memcpy(data, dmadata, len); + kfree(dmadata); + return ret; } static int usb_write(struct i2c_adapter *adapter, int cmd, int value, int index, void *data, int len) { struct i2c_tiny_usb *dev = (struct i2c_tiny_usb *)adapter->algo_data; + void *dmadata = kmemdup(data, len, GFP_KERNEL); + int ret; + + if (!dmadata) + return -ENOMEM; /* do control transfer */ - return usb_control_msg(dev->usb_dev, usb_sndctrlpipe(dev->usb_dev, 0), + ret = usb_control_msg(dev->usb_dev, usb_sndctrlpipe(dev->usb_dev, 0), cmd, USB_TYPE_VENDOR | USB_RECIP_INTERFACE, - value, index, data, len, 2000); + value, index, dmadata, len, 2000); + + kfree(dmadata); + return ret; } static void i2c_tiny_usb_free(struct i2c_tiny_usb *dev) diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index e690dd11e99f..4b0f942b8914 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -184,9 +184,9 @@ static const struct iio_chan_spec_ext_info ad7303_ext_info[] = { .address = (chan), \ .scan_type = { \ .sign = 'u', \ - .realbits = '8', \ - .storagebits = '8', \ - .shift = '0', \ + .realbits = 8, \ + .storagebits = 8, \ + .shift = 0, \ }, \ .ext_info = ad7303_ext_info, \ } diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 6bf89d8f3741..b9d1e5c58ec5 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -74,9 +74,9 @@ static const int int_time_mapping[] = {100000, 50000, 200000, 400000}; static const struct reg_field reg_field_it = REG_FIELD(LTR501_ALS_MEAS_RATE, 3, 4); static const struct reg_field reg_field_als_intr = - REG_FIELD(LTR501_INTR, 0, 0); -static const struct reg_field reg_field_ps_intr = REG_FIELD(LTR501_INTR, 1, 1); +static const struct reg_field reg_field_ps_intr = + REG_FIELD(LTR501_INTR, 0, 0); static const struct reg_field reg_field_als_rate = REG_FIELD(LTR501_ALS_MEAS_RATE, 0, 2); static const struct reg_field reg_field_ps_rate = diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index a0aedda7dfd7..420478924a0c 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -40,9 +40,9 @@ #define AS3935_AFE_PWR_BIT BIT(0) #define AS3935_INT 0x03 -#define AS3935_INT_MASK 0x07 +#define AS3935_INT_MASK 0x0f #define AS3935_EVENT_INT BIT(3) -#define AS3935_NOISE_INT BIT(1) +#define AS3935_NOISE_INT BIT(0) #define AS3935_DATA 0x07 #define AS3935_DATA_MASK 0x3F @@ -50,7 +50,6 @@ #define AS3935_TUNE_CAP 0x08 #define AS3935_CALIBRATE 0x3D -#define AS3935_WRITE_DATA BIT(15) #define AS3935_READ_DATA BIT(14) #define AS3935_ADDRESS(x) ((x) << 8) @@ -105,7 +104,7 @@ static int as3935_write(struct as3935_state *st, { u8 *buf = st->buf; - buf[0] = (AS3935_WRITE_DATA | AS3935_ADDRESS(reg)) >> 8; + buf[0] = AS3935_ADDRESS(reg) >> 8; buf[1] = val; return spi_write(st->spi, buf, 2); @@ -264,8 +263,6 @@ static irqreturn_t as3935_interrupt_handler(int irq, void *private) static void calibrate_as3935(struct as3935_state *st) { - mutex_lock(&st->lock); - /* mask disturber interrupt bit */ as3935_write(st, AS3935_INT, BIT(5)); @@ -275,8 +272,6 @@ static void calibrate_as3935(struct as3935_state *st) mdelay(2); as3935_write(st, AS3935_TUNE_CAP, (st->tune_cap / TUNE_CAP_DIV)); - - mutex_unlock(&st->lock); } #ifdef CONFIG_PM_SLEEP @@ -313,6 +308,8 @@ static int as3935_resume(struct device *dev) val &= ~AS3935_AFE_PWR_BIT; ret = as3935_write(st, AS3935_AFE_GAIN, val); + calibrate_as3935(st); + err_resume: mutex_unlock(&st->lock); diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index 34b1adad07aa..6a8024d9d742 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -277,8 +277,8 @@ static int addr6_resolve(struct sockaddr_in6 *src_in, fl6.saddr = src_in->sin6_addr; fl6.flowi6_oif = addr->bound_dev_if; - dst = ip6_route_output(addr->net, NULL, &fl6); - if ((ret = dst->error)) + ret = ipv6_stub->ipv6_dst_lookup(addr->net, NULL, &dst, &fl6); + if (ret < 0) goto put; if (ipv6_addr_any(&fl6.saddr)) { diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index b1f37d4095fa..e76d52a203a7 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -863,7 +863,7 @@ err_put: free_port_list_attributes(device); err_unregister: - device_unregister(class_dev); + device_del(class_dev); err: return ret; diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 77ddf2fa8625..8763fb832b01 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -2491,6 +2491,7 @@ err_counter: mlx4_ib_delete_counters_table(ibdev, &ibdev->counters_table[i]); err_map: + mlx4_ib_free_eqs(dev, ibdev); iounmap(ibdev->uar_map); err_uar: diff --git a/drivers/infiniband/hw/mlx4/mcg.c b/drivers/infiniband/hw/mlx4/mcg.c index 36ec8aa048aa..0b5bb0cee6f9 100644 --- a/drivers/infiniband/hw/mlx4/mcg.c +++ b/drivers/infiniband/hw/mlx4/mcg.c @@ -1105,7 +1105,8 @@ static void _mlx4_ib_mcg_port_cleanup(struct mlx4_ib_demux_ctx *ctx, int destroy while ((p = rb_first(&ctx->mcg_table)) != NULL) { group = rb_entry(p, struct mcast_group, node); if (atomic_read(&group->refcount)) - mcg_warn_group(group, "group refcount %d!!! (pointer %p)\n", atomic_read(&group->refcount), group); + mcg_debug_group(group, "group refcount %d!!! (pointer %p)\n", + atomic_read(&group->refcount), group); force_clean_group(group); } diff --git a/drivers/infiniband/hw/qib/qib_rc.c b/drivers/infiniband/hw/qib/qib_rc.c index e6b7556d5221..cbc4216091c9 100644 --- a/drivers/infiniband/hw/qib/qib_rc.c +++ b/drivers/infiniband/hw/qib/qib_rc.c @@ -2088,8 +2088,10 @@ send_last: ret = qib_get_rwqe(qp, 1); if (ret < 0) goto nack_op_err; - if (!ret) + if (!ret) { + qib_put_ss(&qp->r_sge); goto rnr_nak; + } wc.ex.imm_data = ohdr->u.rc.imm_data; hdrsize += 4; wc.wc_flags = IB_WC_WITH_IMM; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_fs.c b/drivers/infiniband/ulp/ipoib/ipoib_fs.c index 6bd5740e2691..09396bd7b02d 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_fs.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_fs.c @@ -281,8 +281,11 @@ void ipoib_delete_debug_files(struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); + WARN_ONCE(!priv->mcg_dentry, "null mcg debug file\n"); + WARN_ONCE(!priv->path_dentry, "null path debug file\n"); debugfs_remove(priv->mcg_dentry); debugfs_remove(priv->path_dentry); + priv->mcg_dentry = priv->path_dentry = NULL; } int ipoib_register_debugfs(void) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 8efcff1beb8f..6699ecd855f0 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -106,6 +106,33 @@ static struct ib_client ipoib_client = { .get_net_dev_by_params = ipoib_get_net_dev_by_params, }; +#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG +static int ipoib_netdev_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct netdev_notifier_info *ni = ptr; + struct net_device *dev = ni->dev; + + if (dev->netdev_ops->ndo_open != ipoib_open) + return NOTIFY_DONE; + + switch (event) { + case NETDEV_REGISTER: + ipoib_create_debug_files(dev); + break; + case NETDEV_CHANGENAME: + ipoib_delete_debug_files(dev); + ipoib_create_debug_files(dev); + break; + case NETDEV_UNREGISTER: + ipoib_delete_debug_files(dev); + break; + } + + return NOTIFY_DONE; +} +#endif + int ipoib_open(struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); @@ -1595,8 +1622,6 @@ void ipoib_dev_cleanup(struct net_device *dev) ASSERT_RTNL(); - ipoib_delete_debug_files(dev); - /* Delete any child interfaces first */ list_for_each_entry_safe(cpriv, tcpriv, &priv->child_intfs, list) { /* Stop GC on child */ @@ -1908,8 +1933,6 @@ static struct net_device *ipoib_add_port(const char *format, goto register_failed; } - ipoib_create_debug_files(priv->dev); - if (ipoib_cm_add_mode_attr(priv->dev)) goto sysfs_failed; if (ipoib_add_pkey_attr(priv->dev)) @@ -1924,7 +1947,6 @@ static struct net_device *ipoib_add_port(const char *format, return priv->dev; sysfs_failed: - ipoib_delete_debug_files(priv->dev); unregister_netdev(priv->dev); register_failed: @@ -2006,6 +2028,12 @@ static void ipoib_remove_one(struct ib_device *device, void *client_data) kfree(dev_list); } +#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG +static struct notifier_block ipoib_netdev_notifier = { + .notifier_call = ipoib_netdev_event, +}; +#endif + static int __init ipoib_init_module(void) { int ret; @@ -2057,6 +2085,9 @@ static int __init ipoib_init_module(void) if (ret) goto err_client; +#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG + register_netdevice_notifier(&ipoib_netdev_notifier); +#endif return 0; err_client: @@ -2074,6 +2105,9 @@ err_fs: static void __exit ipoib_cleanup_module(void) { +#ifdef CONFIG_INFINIBAND_IPOIB_DEBUG + unregister_netdevice_notifier(&ipoib_netdev_notifier); +#endif ipoib_netlink_fini(); ib_unregister_client(&ipoib_client); ib_sa_unregister_client(&ipoib_sa_client); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c index fca1a882de27..57a34f87dedf 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c @@ -85,8 +85,6 @@ int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv, goto register_failed; } - ipoib_create_debug_files(priv->dev); - /* RTNL childs don't need proprietary sysfs entries */ if (type == IPOIB_LEGACY_CHILD) { if (ipoib_cm_add_mode_attr(priv->dev)) @@ -107,7 +105,6 @@ int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv, sysfs_failed: result = -ENOMEM; - ipoib_delete_debug_files(priv->dev); unregister_netdevice(priv->dev); register_failed: diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index 1a2b2620421e..6f4dc0fd2ca3 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1122,8 +1122,10 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse, * Asus UX32VD 0x361f02 00, 15, 0e clickpad * Avatar AVIU-145A2 0x361f00 ? clickpad * Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons + * Fujitsu LIFEBOOK E546 0x470f00 50, 12, 09 2 hw buttons * Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons * Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons + * Fujitsu LIFEBOOK E557 0x570f01 40, 14, 0c 2 hw buttons * Fujitsu T725 0x470f01 05, 12, 09 2 hw buttons * Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**) * Gigabyte U2442 0x450f01 58, 17, 0c 2 hw buttons @@ -1529,6 +1531,13 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = { }, }, { + /* Fujitsu LIFEBOOK E546 does not work with crc_enabled == 0 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E546"), + }, + }, + { /* Fujitsu LIFEBOOK E547 does not work with crc_enabled == 0 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), @@ -1550,6 +1559,13 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = { }, }, { + /* Fujitsu LIFEBOOK E557 does not work with crc_enabled == 0 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E557"), + }, + }, + { /* Fujitsu LIFEBOOK U745 does not work with crc_enabled == 0 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), diff --git a/drivers/input/touchscreen/synaptics_dsx/Kconfig b/drivers/input/touchscreen/synaptics_dsx/Kconfig index 86263fddacea..18d473969261 100644 --- a/drivers/input/touchscreen/synaptics_dsx/Kconfig +++ b/drivers/input/touchscreen/synaptics_dsx/Kconfig @@ -50,17 +50,6 @@ config TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21 To compile this driver as a module, choose M here: the module will be called synaptics_dsx_core. -config TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21 - tristate "Synaptics DSX touchscreen RMI device module" - depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21 - help - Say Y here to enable support for direct RMI register access. - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called synaptics_dsx_rmi_dev. - config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21 tristate "Synaptics DSX touchscreen firmware update module" depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21 @@ -72,15 +61,4 @@ config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21 To compile this driver as a module, choose M here: the module will be called synaptics_dsx_fw_update. -config TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY_v21 - tristate "Synaptics DSX touchscreen proximity module" - depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21 - help - Say Y here to enable support for proximity functionalities. - - If unsure, say N. - - To compile this driver as a module, choose M here: the - module will be called synaptics_dsx_proximity. - endif diff --git a/drivers/input/touchscreen/synaptics_dsx/Makefile b/drivers/input/touchscreen/synaptics_dsx/Makefile index b35b222d5ae2..0bffb8da94ea 100644 --- a/drivers/input/touchscreen/synaptics_dsx/Makefile +++ b/drivers/input/touchscreen/synaptics_dsx/Makefile @@ -7,6 +7,4 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_I2C_v21) += synaptics_dsx_i2c.o obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_SPI_v21) += synaptics_dsx_spi.o obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v21) += synaptics_dsx_core.o -obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v21) += synaptics_dsx_rmi_dev.o obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v21) += synaptics_dsx_fw_update.o -obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY_v21) += synaptics_dsx_proximity.o diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c index daa35845fc0a..93c9c3c373b8 100644 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c +++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c @@ -1495,7 +1495,7 @@ static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data, return retval; } -static void synaptics_rmi4_set_intr_mask(struct synaptics_rmi4_fn *fhandler, +static int synaptics_rmi4_set_intr_mask(struct synaptics_rmi4_fn *fhandler, struct synaptics_rmi4_fn_desc *fd, unsigned int intr_count) { @@ -1503,6 +1503,12 @@ static void synaptics_rmi4_set_intr_mask(struct synaptics_rmi4_fn *fhandler, unsigned char intr_offset; fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) { + fhandler->intr_reg_num = 0; + fhandler->num_of_data_sources = 0; + fhandler->intr_mask = 0; + return -EINVAL; + } if (fhandler->intr_reg_num != 0) fhandler->intr_reg_num -= 1; @@ -1515,7 +1521,7 @@ static void synaptics_rmi4_set_intr_mask(struct synaptics_rmi4_fn *fhandler, ii++) fhandler->intr_mask |= 1 << ii; - return; + return 0; } static int synaptics_rmi4_f01_init(struct synaptics_rmi4_data *rmi4_data, @@ -1523,12 +1529,17 @@ static int synaptics_rmi4_f01_init(struct synaptics_rmi4_data *rmi4_data, struct synaptics_rmi4_fn_desc *fd, unsigned int intr_count) { + int retval; + fhandler->fn_number = fd->fn_number; fhandler->num_of_data_sources = fd->intr_src_count; fhandler->data = NULL; fhandler->extra = NULL; - synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + retval = synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + if (retval < 0) + return retval; + rmi4_data->f01_query_base_addr = fd->query_base_addr; rmi4_data->f01_ctrl_base_addr = fd->ctrl_base_addr; @@ -1653,7 +1664,9 @@ static int synaptics_rmi4_f11_init(struct synaptics_rmi4_data *rmi4_data, if (retval < 0) return retval; - synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + retval = synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + if (retval < 0) + return retval; abs_data_size = query[5] & MASK_2BIT; abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0)); @@ -1934,7 +1947,9 @@ static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data, if (retval < 0) goto free_function_handler_mem; - synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + retval = synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + if (retval < 0) + return retval; /* Allocate memory for finger data storage space */ fhandler->data_size = num_of_fingers * size_of_2d_data; @@ -2092,7 +2107,9 @@ static int synaptics_rmi4_f1a_init(struct synaptics_rmi4_data *rmi4_data, fhandler->fn_number = fd->fn_number; fhandler->num_of_data_sources = fd->intr_src_count; - synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + retval = synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count); + if (retval < 0) + return retval; retval = synaptics_rmi4_f1a_alloc_mem(rmi4_data, fhandler); if (retval < 0) @@ -2491,6 +2508,8 @@ flash_prog_mode: dev_dbg(rmi4_data->pdev->dev.parent, "%s: Number of interrupt registers = %d\n", __func__, rmi4_data->num_of_intr_regs); + if (rmi4_data->num_of_intr_regs >= MAX_INTR_REGISTERS) + return -EINVAL; retval = synaptics_rmi4_reg_read(rmi4_data, rmi4_data->f01_query_base_addr, diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_proximity.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_proximity.c deleted file mode 100755 index 99c05e6845c0..000000000000 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_proximity.c +++ /dev/null @@ -1,671 +0,0 @@ -/* - * Synaptics DSX touchscreen driver - * - * Copyright (C) 2012 Synaptics Incorporated - * - * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> - * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/input.h> -#include <linux/platform_device.h> -#include <linux/input/synaptics_dsx_v2.h> -#include "synaptics_dsx_core.h" - -#define PROX_PHYS_NAME "synaptics_dsx/input1" - -#define HOVER_Z_MAX (255) - -#define HOVERING_FINGER_EN (1 << 4) - -static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev, - struct device_attribute *attr, char *buf); - -static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count); - -static struct device_attribute attrs[] = { - __ATTR(hover_finger_en, (S_IRUGO | S_IWUGO), - synaptics_rmi4_hover_finger_en_show, - synaptics_rmi4_hover_finger_en_store), -}; - -struct synaptics_rmi4_f12_query_5 { - union { - struct { - unsigned char size_of_query6; - struct { - unsigned char ctrl0_is_present:1; - unsigned char ctrl1_is_present:1; - unsigned char ctrl2_is_present:1; - unsigned char ctrl3_is_present:1; - unsigned char ctrl4_is_present:1; - unsigned char ctrl5_is_present:1; - unsigned char ctrl6_is_present:1; - unsigned char ctrl7_is_present:1; - } __packed; - struct { - unsigned char ctrl8_is_present:1; - unsigned char ctrl9_is_present:1; - unsigned char ctrl10_is_present:1; - unsigned char ctrl11_is_present:1; - unsigned char ctrl12_is_present:1; - unsigned char ctrl13_is_present:1; - unsigned char ctrl14_is_present:1; - unsigned char ctrl15_is_present:1; - } __packed; - struct { - unsigned char ctrl16_is_present:1; - unsigned char ctrl17_is_present:1; - unsigned char ctrl18_is_present:1; - unsigned char ctrl19_is_present:1; - unsigned char ctrl20_is_present:1; - unsigned char ctrl21_is_present:1; - unsigned char ctrl22_is_present:1; - unsigned char ctrl23_is_present:1; - } __packed; - }; - unsigned char data[4]; - }; -}; - -struct synaptics_rmi4_f12_query_8 { - union { - struct { - unsigned char size_of_query9; - struct { - unsigned char data0_is_present:1; - unsigned char data1_is_present:1; - unsigned char data2_is_present:1; - unsigned char data3_is_present:1; - unsigned char data4_is_present:1; - unsigned char data5_is_present:1; - unsigned char data6_is_present:1; - unsigned char data7_is_present:1; - } __packed; - }; - unsigned char data[2]; - }; -}; - -struct prox_finger_data { - union { - struct { - unsigned char object_type_and_status; - unsigned char x_lsb; - unsigned char x_msb; - unsigned char y_lsb; - unsigned char y_msb; - unsigned char z; - } __packed; - unsigned char proximity_data[6]; - }; -}; - -struct synaptics_rmi4_prox_handle { - bool hover_finger_present; - bool hover_finger_en; - unsigned char intr_mask; - unsigned short query_base_addr; - unsigned short control_base_addr; - unsigned short data_base_addr; - unsigned short command_base_addr; - unsigned short hover_finger_en_addr; - unsigned short hover_finger_data_addr; - struct input_dev *prox_dev; - struct prox_finger_data *finger_data; - struct synaptics_rmi4_data *rmi4_data; -}; - -static struct synaptics_rmi4_prox_handle *prox; - -DECLARE_COMPLETION(prox_remove_complete); - -static void prox_hover_finger_lift(void) -{ - input_report_key(prox->prox_dev, BTN_TOUCH, 0); - input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 0); - input_sync(prox->prox_dev); - prox->hover_finger_present = false; - - return; -} - -static void prox_hover_finger_report(void) -{ - int retval; - int x; - int y; - int z; - struct prox_finger_data *data; - struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; - - data = prox->finger_data; - - retval = synaptics_rmi4_reg_read(rmi4_data, - prox->hover_finger_data_addr, - data->proximity_data, - sizeof(data->proximity_data)); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to read hovering finger data\n", - __func__); - return; - } - - if (data->object_type_and_status != F12_HOVERING_FINGER_STATUS) { - if (prox->hover_finger_present) - prox_hover_finger_lift(); - - return; - } - - x = (data->x_msb << 8) | (data->x_lsb); - y = (data->y_msb << 8) | (data->y_lsb); - z = HOVER_Z_MAX - data->z; - - input_report_key(prox->prox_dev, BTN_TOUCH, 0); - input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 1); - input_report_abs(prox->prox_dev, ABS_X, x); - input_report_abs(prox->prox_dev, ABS_Y, y); - input_report_abs(prox->prox_dev, ABS_DISTANCE, z); - - input_sync(prox->prox_dev); - - dev_dbg(rmi4_data->pdev->dev.parent, - "%s: x = %d y = %d z = %d\n", - __func__, x, y, z); - - prox->hover_finger_present = true; - - return; -} - -static int prox_set_hover_finger_en(void) -{ - int retval; - unsigned char object_report_enable; - struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; - - retval = synaptics_rmi4_reg_read(rmi4_data, - prox->hover_finger_en_addr, - &object_report_enable, - sizeof(object_report_enable)); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to read from object report enable register\n", - __func__); - return retval; - } - - if (prox->hover_finger_en) - object_report_enable |= HOVERING_FINGER_EN; - else - object_report_enable &= ~HOVERING_FINGER_EN; - - retval = synaptics_rmi4_reg_write(rmi4_data, - prox->hover_finger_en_addr, - &object_report_enable, - sizeof(object_report_enable)); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to write to object report enable register\n", - __func__); - return retval; - } - - return 0; -} - -static void prox_set_params(void) -{ - input_set_abs_params(prox->prox_dev, ABS_X, 0, - prox->rmi4_data->sensor_max_x, 0, 0); - input_set_abs_params(prox->prox_dev, ABS_Y, 0, - prox->rmi4_data->sensor_max_y, 0, 0); - input_set_abs_params(prox->prox_dev, ABS_DISTANCE, 0, - HOVER_Z_MAX, 0, 0); - - return; -} - -static int prox_reg_init(void) -{ - int retval; - unsigned char ctrl_23_offset; - unsigned char data_1_offset; - struct synaptics_rmi4_f12_query_5 query_5; - struct synaptics_rmi4_f12_query_8 query_8; - struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; - - retval = synaptics_rmi4_reg_read(rmi4_data, - prox->query_base_addr + 5, - query_5.data, - sizeof(query_5.data)); - if (retval < 0) - return retval; - - ctrl_23_offset = query_5.ctrl0_is_present + - query_5.ctrl1_is_present + - query_5.ctrl2_is_present + - query_5.ctrl3_is_present + - query_5.ctrl4_is_present + - query_5.ctrl5_is_present + - query_5.ctrl6_is_present + - query_5.ctrl7_is_present + - query_5.ctrl8_is_present + - query_5.ctrl9_is_present + - query_5.ctrl10_is_present + - query_5.ctrl11_is_present + - query_5.ctrl12_is_present + - query_5.ctrl13_is_present + - query_5.ctrl14_is_present + - query_5.ctrl15_is_present + - query_5.ctrl16_is_present + - query_5.ctrl17_is_present + - query_5.ctrl18_is_present + - query_5.ctrl19_is_present + - query_5.ctrl20_is_present + - query_5.ctrl21_is_present + - query_5.ctrl22_is_present; - - prox->hover_finger_en_addr = prox->control_base_addr + ctrl_23_offset; - - retval = synaptics_rmi4_reg_read(rmi4_data, - prox->query_base_addr + 8, - query_8.data, - sizeof(query_8.data)); - if (retval < 0) - return retval; - - data_1_offset = query_8.data0_is_present; - prox->hover_finger_data_addr = prox->data_base_addr + data_1_offset; - - return retval; -} - -static int prox_scan_pdt(void) -{ - int retval; - unsigned char ii; - unsigned char page; - unsigned char intr_count = 0; - unsigned char intr_off; - unsigned char intr_src; - unsigned short addr; - struct synaptics_rmi4_fn_desc fd; - struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; - - for (page = 0; page < PAGES_TO_SERVICE; page++) { - for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { - addr |= (page << 8); - - retval = synaptics_rmi4_reg_read(rmi4_data, - addr, - (unsigned char *)&fd, - sizeof(fd)); - if (retval < 0) - return retval; - - addr &= ~(MASK_8BIT << 8); - - if (fd.fn_number) { - dev_dbg(rmi4_data->pdev->dev.parent, - "%s: Found F%02x\n", - __func__, fd.fn_number); - switch (fd.fn_number) { - case SYNAPTICS_RMI4_F12: - goto f12_found; - break; - } - } else { - break; - } - - intr_count += (fd.intr_src_count & MASK_3BIT); - } - } - - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to find F12\n", - __func__); - return -EINVAL; - -f12_found: - prox->query_base_addr = fd.query_base_addr | (page << 8); - prox->control_base_addr = fd.ctrl_base_addr | (page << 8); - prox->data_base_addr = fd.data_base_addr | (page << 8); - prox->command_base_addr = fd.cmd_base_addr | (page << 8); - - retval = prox_reg_init(); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to initialize proximity registers\n", - __func__); - return retval; - } - - prox->intr_mask = 0; - intr_src = fd.intr_src_count; - intr_off = intr_count % 8; - for (ii = intr_off; - ii < ((intr_src & MASK_3BIT) + - intr_off); - ii++) { - prox->intr_mask |= 1 << ii; - } - - rmi4_data->intr_mask[0] |= prox->intr_mask; - - addr = rmi4_data->f01_ctrl_base_addr + 1; - - retval = synaptics_rmi4_reg_write(rmi4_data, - addr, - &(rmi4_data->intr_mask[0]), - sizeof(rmi4_data->intr_mask[0])); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to set interrupt enable bit\n", - __func__); - return retval; - } - - return 0; -} - -static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - if (!prox) - return -ENODEV; - - return snprintf(buf, PAGE_SIZE, "%u\n", - prox->hover_finger_en); -} - -static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int retval; - unsigned int input; - struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data; - - if (!prox) - return -ENODEV; - - if (sscanf(buf, "%x", &input) != 1) - return -EINVAL; - - if (input == 1) - prox->hover_finger_en = true; - else if (input == 0) - prox->hover_finger_en = false; - else - return -EINVAL; - - retval = prox_set_hover_finger_en(); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to change hovering finger enable setting\n", - __func__); - return retval; - } - - return count; -} - -int synaptics_rmi4_prox_hover_finger_en(bool enable) -{ - int retval; - - if (!prox) - return -ENODEV; - - prox->hover_finger_en = enable; - - retval = prox_set_hover_finger_en(); - if (retval < 0) - return retval; - - return 0; -} -EXPORT_SYMBOL(synaptics_rmi4_prox_hover_finger_en); - -static void synaptics_rmi4_prox_attn(struct synaptics_rmi4_data *rmi4_data, - unsigned char intr_mask) -{ - if (!prox) - return; - - if (prox->intr_mask & intr_mask) - prox_hover_finger_report(); - - return; -} - -static int synaptics_rmi4_prox_init(struct synaptics_rmi4_data *rmi4_data) -{ - int retval; - unsigned char attr_count; - - prox = kzalloc(sizeof(*prox), GFP_KERNEL); - if (!prox) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to alloc mem for prox\n", - __func__); - retval = -ENOMEM; - goto exit; - } - - prox->finger_data = kzalloc(sizeof(*(prox->finger_data)), GFP_KERNEL); - if (!prox->finger_data) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to alloc mem for finger_data\n", - __func__); - retval = -ENOMEM; - goto exit_free_prox; - } - - prox->rmi4_data = rmi4_data; - - retval = prox_scan_pdt(); - if (retval < 0) - goto exit_free_finger_data; - - prox->hover_finger_en = true; - - retval = prox_set_hover_finger_en(); - if (retval < 0) - return retval; - - prox->prox_dev = input_allocate_device(); - if (prox->prox_dev == NULL) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to allocate proximity device\n", - __func__); - retval = -ENOMEM; - goto exit_free_finger_data; - } - - prox->prox_dev->name = PLATFORM_DRIVER_NAME; - prox->prox_dev->phys = PROX_PHYS_NAME; - prox->prox_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT; - prox->prox_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION; - prox->prox_dev->dev.parent = rmi4_data->pdev->dev.parent; - input_set_drvdata(prox->prox_dev, rmi4_data); - - set_bit(EV_KEY, prox->prox_dev->evbit); - set_bit(EV_ABS, prox->prox_dev->evbit); - set_bit(BTN_TOUCH, prox->prox_dev->keybit); - set_bit(BTN_TOOL_FINGER, prox->prox_dev->keybit); -#ifdef INPUT_PROP_DIRECT - set_bit(INPUT_PROP_DIRECT, prox->prox_dev->propbit); -#endif - - prox_set_params(); - - retval = input_register_device(prox->prox_dev); - if (retval) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to register proximity device\n", - __func__); - goto exit_free_input_device; - } - - for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { - retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj, - &attrs[attr_count].attr); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to create sysfs attributes\n", - __func__); - goto exit_free_sysfs; - } - } - - return 0; - -exit_free_sysfs: - for (attr_count--; attr_count >= 0; attr_count--) { - sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, - &attrs[attr_count].attr); - } - - input_unregister_device(prox->prox_dev); - prox->prox_dev = NULL; - -exit_free_input_device: - if (prox->prox_dev) - input_free_device(prox->prox_dev); - -exit_free_finger_data: - kfree(prox->finger_data); - -exit_free_prox: - kfree(prox); - prox = NULL; - -exit: - return retval; -} - -static void synaptics_rmi4_prox_remove(struct synaptics_rmi4_data *rmi4_data) -{ - unsigned char attr_count; - - if (!prox) - goto exit; - - for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { - sysfs_remove_file(&rmi4_data->input_dev->dev.kobj, - &attrs[attr_count].attr); - } - - input_unregister_device(prox->prox_dev); - kfree(prox->finger_data); - kfree(prox); - prox = NULL; - -exit: - complete(&prox_remove_complete); - - return; -} - -static void synaptics_rmi4_prox_reset(struct synaptics_rmi4_data *rmi4_data) -{ - if (!prox) - return; - - prox_hover_finger_lift(); - - prox_scan_pdt(); - - prox_set_hover_finger_en(); - - prox_set_params(); - - return; -} - -static void synaptics_rmi4_prox_reinit(struct synaptics_rmi4_data *rmi4_data) -{ - if (!prox) - return; - - prox_hover_finger_lift(); - - prox_set_hover_finger_en(); - - return; -} - -static void synaptics_rmi4_prox_e_suspend(struct synaptics_rmi4_data *rmi4_data) -{ - if (!prox) - return; - - prox_hover_finger_lift(); - - return; -} - -static void synaptics_rmi4_prox_suspend(struct synaptics_rmi4_data *rmi4_data) -{ - if (!prox) - return; - - prox_hover_finger_lift(); - - return; -} - -static struct synaptics_rmi4_exp_fn proximity_module = { - .fn_type = RMI_PROXIMITY, - .init = synaptics_rmi4_prox_init, - .remove = synaptics_rmi4_prox_remove, - .reset = synaptics_rmi4_prox_reset, - .reinit = synaptics_rmi4_prox_reinit, - .early_suspend = synaptics_rmi4_prox_e_suspend, - .suspend = synaptics_rmi4_prox_suspend, - .resume = NULL, - .late_resume = NULL, - .attn = synaptics_rmi4_prox_attn, -}; - -static int __init rmi4_proximity_module_init(void) -{ - synaptics_rmi4_dsx_new_function(&proximity_module, true); - - return 0; -} - -static void __exit rmi4_proximity_module_exit(void) -{ - synaptics_rmi4_dsx_new_function(&proximity_module, false); - - wait_for_completion(&prox_remove_complete); - - return; -} - -module_init(rmi4_proximity_module_init); -module_exit(rmi4_proximity_module_exit); - -MODULE_AUTHOR("Synaptics, Inc."); -MODULE_DESCRIPTION("Synaptics DSX Proximity Module"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c deleted file mode 100644 index c1cbec81d7d6..000000000000 --- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c +++ /dev/null @@ -1,810 +0,0 @@ -/* - * Synaptics DSX touchscreen driver - * - * Copyright (C) 2012 Synaptics Incorporated - * - * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> - * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/input.h> -#include <linux/gpio.h> -#include <linux/uaccess.h> -#include <linux/cdev.h> -#include <linux/platform_device.h> -#include <linux/input/synaptics_dsx_v2.h> -#include "synaptics_dsx_core.h" - -#define CHAR_DEVICE_NAME "rmi" -#define DEVICE_CLASS_NAME "rmidev" -#define SYSFS_FOLDER_NAME "rmidev" -#define DEV_NUMBER 1 -#define REG_ADDR_LIMIT 0xFFFF - -static ssize_t rmidev_sysfs_data_show(struct file *data_file, - struct kobject *kobj, struct bin_attribute *attributes, - char *buf, loff_t pos, size_t count); - -static ssize_t rmidev_sysfs_data_store(struct file *data_file, - struct kobject *kobj, struct bin_attribute *attributes, - char *buf, loff_t pos, size_t count); - -static ssize_t rmidev_sysfs_open_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count); - -static ssize_t rmidev_sysfs_release_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count); - -static ssize_t rmidev_sysfs_attn_state_show(struct device *dev, - struct device_attribute *attr, char *buf); - -struct rmidev_handle { - dev_t dev_no; - struct device dev; - struct synaptics_rmi4_data *rmi4_data; - struct kobject *sysfs_dir; - void *data; - bool irq_enabled; -}; - -struct rmidev_data { - int ref_count; - struct cdev main_dev; - struct class *device_class; - struct mutex file_mutex; - struct rmidev_handle *rmi_dev; -}; - -static struct bin_attribute attr_data = { - .attr = { - .name = "data", - .mode = (S_IRUGO | S_IWUSR), - }, - .size = 0, - .read = rmidev_sysfs_data_show, - .write = rmidev_sysfs_data_store, -}; - -static struct device_attribute attrs[] = { - __ATTR(open, S_IWUSR | S_IWGRP, - NULL, - rmidev_sysfs_open_store), - __ATTR(release, S_IWUSR | S_IWGRP, - NULL, - rmidev_sysfs_release_store), - __ATTR(attn_state, S_IRUGO, - rmidev_sysfs_attn_state_show, - synaptics_rmi4_store_error), -}; - -static int rmidev_major_num; - -static struct class *rmidev_device_class; - -static struct rmidev_handle *rmidev; - -DECLARE_COMPLETION(rmidev_remove_complete); - -static irqreturn_t rmidev_sysfs_irq(int irq, void *data) -{ - struct synaptics_rmi4_data *rmi4_data = data; - - sysfs_notify(&rmi4_data->input_dev->dev.kobj, - SYSFS_FOLDER_NAME, "attn_state"); - - return IRQ_HANDLED; -} - -static int rmidev_sysfs_irq_enable(struct synaptics_rmi4_data *rmi4_data, - bool enable) -{ - int retval = 0; - unsigned char intr_status[MAX_INTR_REGISTERS]; - unsigned long irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING; - - if (enable) { - if (rmidev->irq_enabled) - return retval; - - /* Clear interrupts first */ - retval = synaptics_rmi4_reg_read(rmi4_data, - rmi4_data->f01_data_base_addr + 1, - intr_status, - rmi4_data->num_of_intr_regs); - if (retval < 0) - return retval; - - retval = request_threaded_irq(rmi4_data->irq, NULL, - rmidev_sysfs_irq, irq_flags, - "synaptics_dsx_rmidev", rmi4_data); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to create irq thread\n", - __func__); - return retval; - } - - rmidev->irq_enabled = true; - } else { - if (rmidev->irq_enabled) { - disable_irq(rmi4_data->irq); - free_irq(rmi4_data->irq, rmi4_data); - rmidev->irq_enabled = false; - } - } - - return retval; -} - -static ssize_t rmidev_sysfs_data_show(struct file *data_file, - struct kobject *kobj, struct bin_attribute *attributes, - char *buf, loff_t pos, size_t count) -{ - int retval; - unsigned int length = (unsigned int)count; - unsigned short address = (unsigned short)pos; - struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; - - if (length > (REG_ADDR_LIMIT - address)) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Out of register map limit\n", - __func__); - return -EINVAL; - } - - if (length) { - retval = synaptics_rmi4_reg_read(rmi4_data, - address, - (unsigned char *)buf, - length); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to read data\n", - __func__); - return retval; - } - } else { - return -EINVAL; - } - - return length; -} - -static ssize_t rmidev_sysfs_data_store(struct file *data_file, - struct kobject *kobj, struct bin_attribute *attributes, - char *buf, loff_t pos, size_t count) -{ - int retval; - unsigned int length = (unsigned int)count; - unsigned short address = (unsigned short)pos; - struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; - - if (length > (REG_ADDR_LIMIT - address)) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Out of register map limit\n", - __func__); - return -EINVAL; - } - - if (length) { - retval = synaptics_rmi4_reg_write(rmi4_data, - address, - (unsigned char *)buf, - length); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to write data\n", - __func__); - return retval; - } - } else { - return -EINVAL; - } - - return length; -} - -static ssize_t rmidev_sysfs_open_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - unsigned int input; - struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; - - if (sscanf(buf, "%u", &input) != 1) - return -EINVAL; - - if (input != 1) - return -EINVAL; - - rmi4_data->irq_enable(rmi4_data, false); - rmidev_sysfs_irq_enable(rmi4_data, true); - - dev_dbg(rmi4_data->pdev->dev.parent, - "%s: Attention interrupt disabled\n", - __func__); - - return count; -} - -static ssize_t rmidev_sysfs_release_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - unsigned int input; - struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; - - if (sscanf(buf, "%u", &input) != 1) - return -EINVAL; - - if (input != 1) - return -EINVAL; - - rmi4_data->reset_device(rmi4_data); - - rmidev_sysfs_irq_enable(rmi4_data, false); - rmi4_data->irq_enable(rmi4_data, true); - - dev_dbg(rmi4_data->pdev->dev.parent, - "%s: Attention interrupt enabled\n", - __func__); - - return count; -} - -static ssize_t rmidev_sysfs_attn_state_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int attn_state; - struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; - const struct synaptics_dsx_board_data *bdata = - rmi4_data->hw_if->board_data; - - attn_state = gpio_get_value(bdata->irq_gpio); - - return snprintf(buf, PAGE_SIZE, "%u\n", attn_state); -} - -/* - * rmidev_llseek - used to set up register address - * - * @filp: file structure for seek - * @off: offset - * if whence == SEEK_SET, - * high 16 bits: page address - * low 16 bits: register address - * if whence == SEEK_CUR, - * offset from current position - * if whence == SEEK_END, - * offset from end position (0xFFFF) - * @whence: SEEK_SET, SEEK_CUR, or SEEK_END - */ -static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence) -{ - loff_t newpos; - struct rmidev_data *dev_data = filp->private_data; - struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; - - if (IS_ERR(dev_data)) { - pr_err("%s: Pointer of char device data is invalid", __func__); - return -EBADF; - } - - mutex_lock(&(dev_data->file_mutex)); - - switch (whence) { - case SEEK_SET: - newpos = off; - break; - case SEEK_CUR: - newpos = filp->f_pos + off; - break; - case SEEK_END: - newpos = REG_ADDR_LIMIT + off; - break; - default: - newpos = -EINVAL; - goto clean_up; - } - - if (newpos < 0 || newpos > REG_ADDR_LIMIT) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: New position 0x%04x is invalid\n", - __func__, (unsigned int)newpos); - newpos = -EINVAL; - goto clean_up; - } - - filp->f_pos = newpos; - -clean_up: - mutex_unlock(&(dev_data->file_mutex)); - - return newpos; -} - -/* - * rmidev_read: - use to read data from rmi device - * - * @filp: file structure for read - * @buf: user space buffer pointer - * @count: number of bytes to read - * @f_pos: offset (starting register address) - */ -static ssize_t rmidev_read(struct file *filp, char __user *buf, - size_t count, loff_t *f_pos) -{ - ssize_t retval; - unsigned char *tmpbuf; - struct rmidev_data *dev_data = filp->private_data; - - if (IS_ERR(dev_data)) { - pr_err("%s: Pointer of char device data is invalid", __func__); - return -EBADF; - } - - mutex_lock(&(dev_data->file_mutex)); - - if (count > (REG_ADDR_LIMIT - *f_pos)) - count = REG_ADDR_LIMIT - *f_pos; - - if (count == 0) { - retval = 0; - goto unlock; - } - - if (*f_pos > REG_ADDR_LIMIT) { - retval = -EFAULT; - goto unlock; - } - tmpbuf = kzalloc(count + 1, GFP_KERNEL); - if (!tmpbuf) { - retval = -ENOMEM; - goto unlock; - } - retval = synaptics_rmi4_reg_read(rmidev->rmi4_data, - *f_pos, - tmpbuf, - count); - if (retval < 0) - goto clean_up; - - if (copy_to_user(buf, tmpbuf, count)) - retval = -EFAULT; - else - *f_pos += retval; - -clean_up: - kfree(tmpbuf); -unlock: - mutex_unlock(&(dev_data->file_mutex)); - return retval; -} - -/* - * rmidev_write: - used to write data to rmi device - * - * @filep: file structure for write - * @buf: user space buffer pointer - * @count: number of bytes to write - * @f_pos: offset (starting register address) - */ -static ssize_t rmidev_write(struct file *filp, const char __user *buf, - size_t count, loff_t *f_pos) -{ - ssize_t retval; - unsigned char *tmpbuf; - struct rmidev_data *dev_data = filp->private_data; - - if (IS_ERR(dev_data)) { - pr_err("%s: Pointer of char device data is invalid", __func__); - return -EBADF; - } - - mutex_lock(&(dev_data->file_mutex)); - - if (*f_pos > REG_ADDR_LIMIT) { - retval = -EFAULT; - goto unlock; - } - - if (count > (REG_ADDR_LIMIT - *f_pos)) - count = REG_ADDR_LIMIT - *f_pos; - - if (count == 0) { - retval = 0; - goto unlock; - } - - tmpbuf = kzalloc(count + 1, GFP_KERNEL); - if (!tmpbuf) { - retval = -ENOMEM; - goto unlock; - } - - if (copy_from_user(tmpbuf, buf, count)) { - retval = -EFAULT; - goto clean_up; - } - - retval = synaptics_rmi4_reg_write(rmidev->rmi4_data, - *f_pos, - tmpbuf, - count); - if (retval >= 0) - *f_pos += retval; - -clean_up: - kfree(tmpbuf); -unlock: - mutex_unlock(&(dev_data->file_mutex)); - return retval; -} - -/* - * rmidev_open: enable access to rmi device - * @inp: inode struture - * @filp: file structure - */ -static int rmidev_open(struct inode *inp, struct file *filp) -{ - int retval = 0; - struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; - struct rmidev_data *dev_data = - container_of(inp->i_cdev, struct rmidev_data, main_dev); - - if (!dev_data) - return -EACCES; - - filp->private_data = dev_data; - - mutex_lock(&(dev_data->file_mutex)); - - rmi4_data->irq_enable(rmi4_data, false); - dev_dbg(rmi4_data->pdev->dev.parent, - "%s: Attention interrupt disabled\n", - __func__); - - if (dev_data->ref_count < 1) - dev_data->ref_count++; - else - retval = -EACCES; - - mutex_unlock(&(dev_data->file_mutex)); - - return retval; -} - -/* - * rmidev_release: - release access to rmi device - * @inp: inode structure - * @filp: file structure - */ -static int rmidev_release(struct inode *inp, struct file *filp) -{ - struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; - struct rmidev_data *dev_data = - container_of(inp->i_cdev, struct rmidev_data, main_dev); - - if (!dev_data) - return -EACCES; - - rmi4_data->reset_device(rmi4_data); - - mutex_lock(&(dev_data->file_mutex)); - - dev_data->ref_count--; - if (dev_data->ref_count < 0) - dev_data->ref_count = 0; - - rmi4_data->irq_enable(rmi4_data, true); - dev_dbg(rmi4_data->pdev->dev.parent, - "%s: Attention interrupt enabled\n", - __func__); - - mutex_unlock(&(dev_data->file_mutex)); - - return 0; -} - -static const struct file_operations rmidev_fops = { - .owner = THIS_MODULE, - .llseek = rmidev_llseek, - .read = rmidev_read, - .write = rmidev_write, - .open = rmidev_open, - .release = rmidev_release, -}; - -static void rmidev_device_cleanup(struct rmidev_data *dev_data) -{ - dev_t devno; - struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; - - if (dev_data) { - devno = dev_data->main_dev.dev; - - if (dev_data->device_class) - device_destroy(dev_data->device_class, devno); - - cdev_del(&dev_data->main_dev); - - unregister_chrdev_region(devno, 1); - - dev_dbg(rmi4_data->pdev->dev.parent, - "%s: rmidev device removed\n", - __func__); - } - - return; -} - -static char *rmi_char_devnode(struct device *dev, umode_t *mode) -{ - if (!mode) - return NULL; - - *mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); - - return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev)); -} - -static int rmidev_create_device_class(void) -{ - rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME); - - if (IS_ERR(rmidev_device_class)) { - pr_err("%s: Failed to create /dev/%s\n", - __func__, CHAR_DEVICE_NAME); - return -ENODEV; - } - - rmidev_device_class->devnode = rmi_char_devnode; - - return 0; -} - -static int rmidev_init_device(struct synaptics_rmi4_data *rmi4_data) -{ - int retval; - dev_t dev_no; - unsigned char attr_count; - struct rmidev_data *dev_data; - struct device *device_ptr; - const struct synaptics_dsx_board_data *bdata = - rmi4_data->hw_if->board_data; - - rmidev = kzalloc(sizeof(*rmidev), GFP_KERNEL); - if (!rmidev) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to alloc mem for rmidev\n", - __func__); - retval = -ENOMEM; - goto err_rmidev; - } - - rmidev->rmi4_data = rmi4_data; - - retval = rmidev_create_device_class(); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to create device class\n", - __func__); - goto err_device_class; - } - - if (rmidev_major_num) { - dev_no = MKDEV(rmidev_major_num, DEV_NUMBER); - retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME); - } else { - retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to allocate char device region\n", - __func__); - goto err_device_region; - } - - rmidev_major_num = MAJOR(dev_no); - dev_dbg(rmi4_data->pdev->dev.parent, - "%s: Major number of rmidev = %d\n", - __func__, rmidev_major_num); - } - - dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); - if (!dev_data) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to alloc mem for dev_data\n", - __func__); - retval = -ENOMEM; - goto err_dev_data; - } - - mutex_init(&dev_data->file_mutex); - dev_data->rmi_dev = rmidev; - rmidev->data = dev_data; - - cdev_init(&dev_data->main_dev, &rmidev_fops); - - retval = cdev_add(&dev_data->main_dev, dev_no, 1); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to add rmi char device\n", - __func__); - goto err_char_device; - } - - dev_set_name(&rmidev->dev, "rmidev%d", MINOR(dev_no)); - dev_data->device_class = rmidev_device_class; - - device_ptr = device_create(dev_data->device_class, NULL, dev_no, - NULL, CHAR_DEVICE_NAME"%d", MINOR(dev_no)); - if (IS_ERR(device_ptr)) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to create rmi char device\n", - __func__); - retval = -ENODEV; - goto err_char_device; - } - - retval = gpio_export(bdata->irq_gpio, false); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to export attention gpio\n", - __func__); - } else { - retval = gpio_export_link(&(rmi4_data->input_dev->dev), - "attn", bdata->irq_gpio); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s Failed to create gpio symlink\n", - __func__); - } else { - dev_dbg(rmi4_data->pdev->dev.parent, - "%s: Exported attention gpio %d\n", - __func__, bdata->irq_gpio); - } - } - - rmidev->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME, - &rmi4_data->input_dev->dev.kobj); - if (!rmidev->sysfs_dir) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to create sysfs directory\n", - __func__); - retval = -ENODEV; - goto err_sysfs_dir; - } - - retval = sysfs_create_bin_file(rmidev->sysfs_dir, - &attr_data); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to create sysfs bin file\n", - __func__); - goto err_sysfs_bin; - } - - for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { - retval = sysfs_create_file(rmidev->sysfs_dir, - &attrs[attr_count].attr); - if (retval < 0) { - dev_err(rmi4_data->pdev->dev.parent, - "%s: Failed to create sysfs attributes\n", - __func__); - retval = -ENODEV; - goto err_sysfs_attrs; - } - } - - return 0; - -err_sysfs_attrs: - for (attr_count--; attr_count >= 0; attr_count--) - sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr); - - sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data); - -err_sysfs_bin: - kobject_put(rmidev->sysfs_dir); - -err_sysfs_dir: -err_char_device: - rmidev_device_cleanup(dev_data); - kfree(dev_data); - -err_dev_data: - unregister_chrdev_region(dev_no, 1); - -err_device_region: - class_destroy(rmidev_device_class); - -err_device_class: - kfree(rmidev); - rmidev = NULL; - -err_rmidev: - return retval; -} - -static void rmidev_remove_device(struct synaptics_rmi4_data *rmi4_data) -{ - unsigned char attr_count; - struct rmidev_data *dev_data; - - if (!rmidev) - goto exit; - - for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) - sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr); - - sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data); - - kobject_put(rmidev->sysfs_dir); - - dev_data = rmidev->data; - if (dev_data) { - rmidev_device_cleanup(dev_data); - kfree(dev_data); - } - - unregister_chrdev_region(rmidev->dev_no, 1); - - class_destroy(rmidev_device_class); - - kfree(rmidev); - rmidev = NULL; - -exit: - complete(&rmidev_remove_complete); - - return; -} - -static struct synaptics_rmi4_exp_fn rmidev_module = { - .fn_type = RMI_DEV, - .init = rmidev_init_device, - .remove = rmidev_remove_device, - .reset = NULL, - .reinit = NULL, - .early_suspend = NULL, - .suspend = NULL, - .resume = NULL, - .late_resume = NULL, - .attn = NULL, -}; - -static int __init rmidev_module_init(void) -{ - synaptics_rmi4_dsx_new_function(&rmidev_module, true); - - return 0; -} - -static void __exit rmidev_module_exit(void) -{ - synaptics_rmi4_dsx_new_function(&rmidev_module, false); - - wait_for_completion(&rmidev_remove_complete); - - return; -} - -module_init(rmidev_module_init); -module_exit(rmidev_module_exit); - -MODULE_AUTHOR("Synaptics, Inc."); -MODULE_DESCRIPTION("Synaptics DSX RMI Dev Module"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index 065379c1397f..c6f74b149706 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -367,6 +367,8 @@ struct arm_smmu_device { u32 num_mapping_groups; DECLARE_BITMAP(smr_map, ARM_SMMU_MAX_SMRS); + u32 ubs; + unsigned long va_size; unsigned long ipa_size; unsigned long pa_size; @@ -404,6 +406,7 @@ struct arm_smmu_device { struct msm_bus_scale_pdata *bus_pdata; enum tz_smmu_device_id sec_id; + int regulator_defer; }; struct arm_smmu_cfg { @@ -950,7 +953,8 @@ static int arm_smmu_disable_regulators(struct arm_smmu_device *smmu) arm_smmu_unprepare_clocks(smmu); arm_smmu_unrequest_bus(smmu); if (smmu->gdsc) { - ret = regulator_disable(smmu->gdsc); + ret = regulator_disable_deferred(smmu->gdsc, + smmu->regulator_defer); WARN(ret, "%s: Regulator disable failed\n", dev_name(smmu->dev)); } @@ -1754,6 +1758,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, { int irq, start, ret = 0; unsigned long ias, oas; + int sep = 0; struct io_pgtable_ops *pgtbl_ops; enum io_pgtable_fmt fmt; struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); @@ -1795,9 +1800,27 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, start = smmu->num_s2_context_banks; ias = smmu->va_size; oas = smmu->ipa_size; - if (IS_ENABLED(CONFIG_64BIT)) + if (IS_ENABLED(CONFIG_64BIT)) { fmt = ARM_64_LPAE_S1; - else + + if (quirks & IO_PGTABLE_QUIRK_ARM_TTBR1) { + + /* + * When the UBS id is 5 we know that the bus + * size is 49 bits and that bit 48 is the fixed + * sign extension bit. For any other bus size + * we need to specify the sign extension bit + * and adjust the input size accordingly + */ + + if (smmu->ubs == 5) { + sep = 48; + } else { + sep = ias - 1; + ias--; + } + } + } else fmt = ARM_32_LPAE_S1; break; case ARM_SMMU_DOMAIN_NESTED: @@ -1859,6 +1882,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain, .pgsize_bitmap = arm_smmu_ops.pgsize_bitmap, .ias = ias, .oas = oas, + .sep = sep, .tlb = &arm_smmu_gather_ops, .iommu_dev = smmu->dev, .iova_base = domain->geometry.aperture_start, @@ -3610,6 +3634,12 @@ static int arm_smmu_init_regulators(struct arm_smmu_device *smmu) if (!of_get_property(dev->of_node, "vdd-supply", NULL)) return 0; + if (!of_property_read_u32(dev->of_node, + "qcom,deferred-regulator-disable-delay", + &(smmu->regulator_defer))) + dev_info(dev, "regulator defer delay %d\n", + smmu->regulator_defer); + smmu->gdsc = devm_regulator_get(dev, "vdd"); if (IS_ERR(smmu->gdsc)) return PTR_ERR(smmu->gdsc); @@ -3888,12 +3918,13 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) smmu->va_size = smmu->ipa_size; size = SZ_4K | SZ_2M | SZ_1G; } else { - size = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK; - smmu->va_size = arm_smmu_id_size_to_bits(size); + smmu->ubs = (id >> ID2_UBS_SHIFT) & ID2_UBS_MASK; + + smmu->va_size = arm_smmu_id_size_to_bits(smmu->ubs); #ifndef CONFIG_64BIT smmu->va_size = min(32UL, smmu->va_size); #endif - smmu->va_size = min(36UL, smmu->va_size); + smmu->va_size = min(39UL, smmu->va_size); size = 0; if (id & ID2_PTFS_4K) size |= SZ_4K | SZ_2M | SZ_1G; diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 0628372f3591..b92b8a724efb 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -2005,11 +2005,14 @@ static int domain_context_mapping_one(struct dmar_domain *domain, if (context_copied(context)) { u16 did_old = context_domain_id(context); - if (did_old >= 0 && did_old < cap_ndoms(iommu->cap)) + if (did_old >= 0 && did_old < cap_ndoms(iommu->cap)) { iommu->flush.flush_context(iommu, did_old, (((u16)bus) << 8) | devfn, DMA_CCMD_MASK_NOBIT, DMA_CCMD_DEVICE_INVL); + iommu->flush.flush_iotlb(iommu, did_old, 0, 0, + DMA_TLB_DSI_FLUSH); + } } pgd = domain->pgd; diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index 2d2583c78bdb..7651545e3f2e 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -550,9 +550,18 @@ static inline arm_lpae_iopte *arm_lpae_get_table( { struct io_pgtable_cfg *cfg = &data->iop.cfg; - return ((cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1) && - (iova & (1UL << (cfg->ias - 1)))) ? - data->pgd[1] : data->pgd[0]; + /* + * iovas for TTBR1 will have all the bits set between the input address + * region and the sign extension bit + */ + if (unlikely(cfg->quirks & IO_PGTABLE_QUIRK_ARM_TTBR1)) { + unsigned long mask = GENMASK(cfg->sep, cfg->ias); + + if ((iova & mask) == mask) + return data->pgd[1]; + } + + return data->pgd[0]; } static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova, @@ -1089,26 +1098,26 @@ static u64 arm64_lpae_setup_ttbr1(struct io_pgtable_cfg *cfg, /* Set T1SZ */ reg |= (64ULL - cfg->ias) << ARM_LPAE_TCR_T1SZ_SHIFT; - /* Set the SEP bit based on the size */ - switch (cfg->ias) { - case 32: + switch (cfg->sep) { + case 31: reg |= (ARM_LPAE_TCR_SEP_31 << ARM_LPAE_TCR_SEP_SHIFT); break; - case 36: + case 35: reg |= (ARM_LPAE_TCR_SEP_35 << ARM_LPAE_TCR_SEP_SHIFT); break; - case 40: + case 39: reg |= (ARM_LPAE_TCR_SEP_39 << ARM_LPAE_TCR_SEP_SHIFT); break; - case 42: + case 41: reg |= (ARM_LPAE_TCR_SEP_41 << ARM_LPAE_TCR_SEP_SHIFT); break; - case 44: + case 43: reg |= (ARM_LPAE_TCR_SEP_43 << ARM_LPAE_TCR_SEP_SHIFT); break; - case 48: + case 47: reg |= (ARM_LPAE_TCR_SEP_47 << ARM_LPAE_TCR_SEP_SHIFT); break; + case 48: default: reg |= (ARM_LPAE_TCR_SEP_UPSTREAM << ARM_LPAE_TCR_SEP_SHIFT); break; diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h index 2cf213514221..0326bb6a4afa 100644 --- a/drivers/iommu/io-pgtable.h +++ b/drivers/iommu/io-pgtable.h @@ -67,6 +67,7 @@ struct io_pgtable_cfg { unsigned long pgsize_bitmap; unsigned int ias; unsigned int oas; + int sep; const struct iommu_gather_ops *tlb; struct device *iommu_dev; dma_addr_t iova_base; diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c index b8155c9227d3..c21846c1f8d5 100644 --- a/drivers/iommu/iommu-debug.c +++ b/drivers/iommu/iommu-debug.c @@ -1491,6 +1491,10 @@ static ssize_t iommu_debug_pte_read(struct file *file, char __user *ubuf, ssize_t retval; size_t buflen; + if (kptr_restrict != 0) { + pr_err("kptr_restrict needs to be disabled.\n"); + return -EPERM; + } if (!dev->archdata.mapping) { pr_err("No mapping. Did you already attach?\n"); return -EINVAL; @@ -1610,6 +1614,10 @@ static ssize_t iommu_debug_dma_atos_read(struct file *file, char __user *ubuf, ssize_t retval; size_t buflen; + if (kptr_restrict != 0) { + pr_err("kptr_restrict needs to be disabled.\n"); + return -EPERM; + } if (!dev->archdata.mapping) { pr_err("No mapping. Did you already attach?\n"); return -EINVAL; diff --git a/drivers/irqchip/irq-xtensa-mx.c b/drivers/irqchip/irq-xtensa-mx.c index bb3ac5fe5846..72a391e01011 100644 --- a/drivers/irqchip/irq-xtensa-mx.c +++ b/drivers/irqchip/irq-xtensa-mx.c @@ -142,7 +142,7 @@ static struct irq_chip xtensa_mx_irq_chip = { int __init xtensa_mx_init_legacy(struct device_node *interrupt_parent) { struct irq_domain *root_domain = - irq_domain_add_legacy(NULL, NR_IRQS, 0, 0, + irq_domain_add_legacy(NULL, NR_IRQS - 1, 1, 0, &xtensa_mx_irq_domain_ops, &xtensa_mx_irq_chip); irq_set_default_host(root_domain); diff --git a/drivers/irqchip/irq-xtensa-pic.c b/drivers/irqchip/irq-xtensa-pic.c index 472ae1770964..f728755fa292 100644 --- a/drivers/irqchip/irq-xtensa-pic.c +++ b/drivers/irqchip/irq-xtensa-pic.c @@ -89,7 +89,7 @@ static struct irq_chip xtensa_irq_chip = { int __init xtensa_pic_init_legacy(struct device_node *interrupt_parent) { struct irq_domain *root_domain = - irq_domain_add_legacy(NULL, NR_IRQS, 0, 0, + irq_domain_add_legacy(NULL, NR_IRQS - 1, 1, 0, &xtensa_irq_domain_ops, &xtensa_irq_chip); irq_set_default_host(root_domain); return 0; diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c index a89a92d253ac..564d20079715 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -721,7 +721,8 @@ static int qpnp_flash_led_get_voltage_headroom(struct qpnp_flash_led *led) #define VIN_FLASH_MIN_UV 3300000LL static int qpnp_flash_led_calc_max_current(struct qpnp_flash_led *led) { - int ocv_uv, rbatt_uohm, ibat_now, voltage_hdrm_mv, rc; + int ocv_uv = 0, rbatt_uohm = 0, ibat_now = 0, voltage_hdrm_mv = 0; + int rc = 0; int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw; int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv, vph_flash_vdip; diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 4c15dee0857b..27713412c881 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -374,6 +374,7 @@ config DM_LOG_USERSPACE config DM_RAID tristate "RAID 1/4/5/6/10 target" depends on BLK_DEV_DM + select MD_RAID0 select MD_RAID1 select MD_RAID10 select MD_RAID456 diff --git a/drivers/md/dm-android-verity.c b/drivers/md/dm-android-verity.c index 7cef735a01a7..4f6086970131 100644 --- a/drivers/md/dm-android-verity.c +++ b/drivers/md/dm-android-verity.c @@ -691,7 +691,7 @@ static int android_verity_ctr(struct dm_target *ti, unsigned argc, char **argv) dev_t uninitialized_var(dev); struct android_metadata *metadata = NULL; int err = 0, i, mode; - char *key_id, *table_ptr, dummy, *target_device, + char *key_id = NULL, *table_ptr, dummy, *target_device, *verity_table_args[VERITY_TABLE_ARGS + 2 + VERITY_TABLE_OPT_FEC_ARGS]; /* One for specifying number of opt args and one for mode */ sector_t data_sectors; diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 2dd33085b331..cdceefd0e57d 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -222,7 +222,7 @@ static DEFINE_SPINLOCK(param_spinlock); * Buffers are freed after this timeout */ static unsigned dm_bufio_max_age = DM_BUFIO_DEFAULT_AGE_SECS; -static unsigned dm_bufio_retain_bytes = DM_BUFIO_DEFAULT_RETAIN_BYTES; +static unsigned long dm_bufio_retain_bytes = DM_BUFIO_DEFAULT_RETAIN_BYTES; static unsigned long dm_bufio_peak_allocated; static unsigned long dm_bufio_allocated_kmem_cache; @@ -914,10 +914,11 @@ static void __get_memory_limit(struct dm_bufio_client *c, { unsigned long buffers; - if (ACCESS_ONCE(dm_bufio_cache_size) != dm_bufio_cache_size_latch) { - mutex_lock(&dm_bufio_clients_lock); - __cache_size_refresh(); - mutex_unlock(&dm_bufio_clients_lock); + if (unlikely(ACCESS_ONCE(dm_bufio_cache_size) != dm_bufio_cache_size_latch)) { + if (mutex_trylock(&dm_bufio_clients_lock)) { + __cache_size_refresh(); + mutex_unlock(&dm_bufio_clients_lock); + } } buffers = dm_bufio_cache_size_per_client >> @@ -1513,10 +1514,10 @@ static bool __try_evict_buffer(struct dm_buffer *b, gfp_t gfp) return true; } -static unsigned get_retain_buffers(struct dm_bufio_client *c) +static unsigned long get_retain_buffers(struct dm_bufio_client *c) { - unsigned retain_bytes = ACCESS_ONCE(dm_bufio_retain_bytes); - return retain_bytes / c->block_size; + unsigned long retain_bytes = ACCESS_ONCE(dm_bufio_retain_bytes); + return retain_bytes >> (c->sectors_per_block_bits + SECTOR_SHIFT); } static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan, @@ -1526,7 +1527,7 @@ static unsigned long __scan(struct dm_bufio_client *c, unsigned long nr_to_scan, struct dm_buffer *b, *tmp; unsigned long freed = 0; unsigned long count = nr_to_scan; - unsigned retain_target = get_retain_buffers(c); + unsigned long retain_target = get_retain_buffers(c); for (l = 0; l < LIST_SIZE; l++) { list_for_each_entry_safe_reverse(b, tmp, &c->lru[l], lru_list) { @@ -1752,11 +1753,19 @@ static bool older_than(struct dm_buffer *b, unsigned long age_hz) static void __evict_old_buffers(struct dm_bufio_client *c, unsigned long age_hz) { struct dm_buffer *b, *tmp; - unsigned retain_target = get_retain_buffers(c); - unsigned count; + unsigned long retain_target = get_retain_buffers(c); + unsigned long count; + LIST_HEAD(write_list); dm_bufio_lock(c); + __check_watermark(c, &write_list); + if (unlikely(!list_empty(&write_list))) { + dm_bufio_unlock(c); + __flush_write_list(&write_list); + dm_bufio_lock(c); + } + count = c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY]; list_for_each_entry_safe_reverse(b, tmp, &c->lru[LIST_CLEAN], lru_list) { if (count <= retain_target) @@ -1781,6 +1790,8 @@ static void cleanup_old_buffers(void) mutex_lock(&dm_bufio_clients_lock); + __cache_size_refresh(); + list_for_each_entry(c, &dm_bufio_all_clients, client_list) __evict_old_buffers(c, max_age_hz); @@ -1904,7 +1915,7 @@ MODULE_PARM_DESC(max_cache_size_bytes, "Size of metadata cache"); module_param_named(max_age_seconds, dm_bufio_max_age, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(max_age_seconds, "Max age of a buffer in seconds"); -module_param_named(retain_bytes, dm_bufio_retain_bytes, uint, S_IRUGO | S_IWUSR); +module_param_named(retain_bytes, dm_bufio_retain_bytes, ulong, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(retain_bytes, "Try to keep at least this many bytes cached in memory"); module_param_named(peak_allocated_bytes, dm_bufio_peak_allocated, ulong, S_IRUGO | S_IWUSR); diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c index 3970cda10080..d3c55d7754af 100644 --- a/drivers/md/dm-cache-metadata.c +++ b/drivers/md/dm-cache-metadata.c @@ -1326,17 +1326,19 @@ void dm_cache_metadata_set_stats(struct dm_cache_metadata *cmd, int dm_cache_commit(struct dm_cache_metadata *cmd, bool clean_shutdown) { - int r; + int r = -EINVAL; flags_mutator mutator = (clean_shutdown ? set_clean_shutdown : clear_clean_shutdown); WRITE_LOCK(cmd); + if (cmd->fail_io) + goto out; + r = __commit_transaction(cmd, mutator); if (r) goto out; r = __begin_transaction(cmd); - out: WRITE_UNLOCK(cmd); return r; @@ -1348,7 +1350,8 @@ int dm_cache_get_free_metadata_block_count(struct dm_cache_metadata *cmd, int r = -EINVAL; READ_LOCK(cmd); - r = dm_sm_get_nr_free(cmd->metadata_sm, result); + if (!cmd->fail_io) + r = dm_sm_get_nr_free(cmd->metadata_sm, result); READ_UNLOCK(cmd); return r; @@ -1360,7 +1363,8 @@ int dm_cache_get_metadata_dev_size(struct dm_cache_metadata *cmd, int r = -EINVAL; READ_LOCK(cmd); - r = dm_sm_get_nr_blocks(cmd->metadata_sm, result); + if (!cmd->fail_io) + r = dm_sm_get_nr_blocks(cmd->metadata_sm, result); READ_UNLOCK(cmd); return r; diff --git a/drivers/md/dm-era-target.c b/drivers/md/dm-era-target.c index 665bf3285618..32e76c5ee741 100644 --- a/drivers/md/dm-era-target.c +++ b/drivers/md/dm-era-target.c @@ -961,15 +961,15 @@ static int metadata_commit(struct era_metadata *md) } } - r = save_sm_root(md); + r = dm_tm_pre_commit(md->tm); if (r) { - DMERR("%s: save_sm_root failed", __func__); + DMERR("%s: pre commit failed", __func__); return r; } - r = dm_tm_pre_commit(md->tm); + r = save_sm_root(md); if (r) { - DMERR("%s: pre commit failed", __func__); + DMERR("%s: save_sm_root failed", __func__); return r; } diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index 911ada643364..3b67afda430b 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -485,11 +485,11 @@ static int __write_initial_superblock(struct dm_pool_metadata *pmd) if (r < 0) return r; - r = save_sm_roots(pmd); + r = dm_tm_pre_commit(pmd->tm); if (r < 0) return r; - r = dm_tm_pre_commit(pmd->tm); + r = save_sm_roots(pmd); if (r < 0) return r; diff --git a/drivers/md/dm-verity-avb.c b/drivers/md/dm-verity-avb.c index 88487346c4c6..727aacbb1480 100644 --- a/drivers/md/dm-verity-avb.c +++ b/drivers/md/dm-verity-avb.c @@ -12,11 +12,14 @@ #define DM_MSG_PREFIX "verity-avb" -/* Set via module parameter. */ +/* Set via module parameters. */ static char avb_vbmeta_device[64]; +static char avb_invalidate_on_error[4]; static void invalidate_vbmeta_endio(struct bio *bio) { + if (bio->bi_error) + DMERR("invalidate_vbmeta_endio: error %d", bio->bi_error); complete(bio->bi_private); } @@ -30,20 +33,19 @@ static int invalidate_vbmeta_submit(struct bio *bio, bio->bi_private = &wait; bio->bi_end_io = invalidate_vbmeta_endio; bio->bi_bdev = bdev; + bio->bi_rw = rw; bio->bi_iter.bi_sector = 0; if (access_last_sector) { - sector_t last_sector = (i_size_read(bdev->bd_inode)>>SECTOR_SHIFT) - 1; + sector_t last_sector; + + last_sector = (i_size_read(bdev->bd_inode)>>SECTOR_SHIFT) - 1; bio->bi_iter.bi_sector = last_sector; } - bio->bi_vcnt = 1; - bio->bi_iter.bi_idx = 0; - bio->bi_iter.bi_size = 512; - bio->bi_iter.bi_bvec_done = 0; - bio->bi_rw = rw; - bio->bi_io_vec[0].bv_page = page; - bio->bi_io_vec[0].bv_len = 512; - bio->bi_io_vec[0].bv_offset = 0; + if (!bio_add_page(bio, page, PAGE_SIZE, 0)) { + DMERR("invalidate_vbmeta_submit: bio_add_page error"); + return -EIO; + } submit_bio(rw, bio); /* Wait up to 2 seconds for completion or fail. */ @@ -65,6 +67,9 @@ static int invalidate_vbmeta(dev_t vbmeta_devt) int rw = REQ_SYNC | REQ_SOFTBARRIER | REQ_NOIDLE; int access_last_sector = 0; + DMINFO("invalidate_vbmeta: acting on device %d:%d", + MAJOR(vbmeta_devt), MINOR(vbmeta_devt)); + /* First we open the device for reading. */ dev_mode = FMODE_READ | FMODE_EXCL; bdev = blkdev_get_by_dev(vbmeta_devt, dev_mode, @@ -115,7 +120,7 @@ static int invalidate_vbmeta(dev_t vbmeta_devt) goto failed_to_submit_read; } if (memcmp("AVBf", page_address(page) + offset, 4) != 0) { - DMERR("invalidate_vbmeta called on non-vbmeta partition"); + DMERR("invalidate_vbmeta on non-vbmeta partition"); ret = -EINVAL; goto invalid_header; } @@ -175,6 +180,11 @@ void dm_verity_avb_error_handler(void) DMINFO("AVB error handler called for %s", avb_vbmeta_device); + if (strcmp(avb_invalidate_on_error, "yes") != 0) { + DMINFO("Not configured to invalidate"); + return; + } + if (avb_vbmeta_device[0] == '\0') { DMERR("avb_vbmeta_device parameter not set"); goto fail_no_dev; @@ -215,3 +225,5 @@ MODULE_LICENSE("GPL"); #undef MODULE_PARAM_PREFIX #define MODULE_PARAM_PREFIX "androidboot.vbmeta." module_param_string(device, avb_vbmeta_device, sizeof(avb_vbmeta_device), 0); +module_param_string(invalidate_on_error, avb_invalidate_on_error, + sizeof(avb_invalidate_on_error), 0); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 5d42d8f09421..1a7b11d57256 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2191,8 +2191,11 @@ static void dm_request_fn(struct request_queue *q) tio = tio_from_request(rq); /* Establish tio->ti before queuing work (map_tio_request) */ tio->ti = ti; - queue_kthread_work(&md->kworker, &tio->work); + spin_unlock(q->queue_lock); + if (map_request(tio, rq, md) == DM_MAPIO_REQUEUE) + dm_requeue_original_request(md, rq); BUG_ON(!irqs_disabled()); + spin_lock(q->queue_lock); } goto out; diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c index b1ced58eb5e1..a1a68209bd36 100644 --- a/drivers/md/persistent-data/dm-btree.c +++ b/drivers/md/persistent-data/dm-btree.c @@ -887,8 +887,12 @@ static int find_key(struct ro_spine *s, dm_block_t block, bool find_highest, else *result_key = le64_to_cpu(ro_node(s)->keys[0]); - if (next_block || flags & INTERNAL_NODE) - block = value64(ro_node(s), i); + if (next_block || flags & INTERNAL_NODE) { + if (find_highest) + block = value64(ro_node(s), i); + else + block = value64(ro_node(s), 0); + } } while (flags & INTERNAL_NODE); diff --git a/drivers/md/persistent-data/dm-space-map-disk.c b/drivers/md/persistent-data/dm-space-map-disk.c index ebb280a14325..32adf6b4a9c7 100644 --- a/drivers/md/persistent-data/dm-space-map-disk.c +++ b/drivers/md/persistent-data/dm-space-map-disk.c @@ -142,10 +142,23 @@ static int sm_disk_inc_block(struct dm_space_map *sm, dm_block_t b) static int sm_disk_dec_block(struct dm_space_map *sm, dm_block_t b) { + int r; + uint32_t old_count; enum allocation_event ev; struct sm_disk *smd = container_of(sm, struct sm_disk, sm); - return sm_ll_dec(&smd->ll, b, &ev); + r = sm_ll_dec(&smd->ll, b, &ev); + if (!r && (ev == SM_FREE)) { + /* + * It's only free if it's also free in the last + * transaction. + */ + r = sm_ll_lookup(&smd->old_ll, b, &old_count); + if (!r && !old_count) + smd->nr_allocated_this_transaction--; + } + + return r; } static int sm_disk_new_block(struct dm_space_map *sm, dm_block_t *b) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 7af976934441..4384b46cee1a 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -2232,6 +2232,10 @@ static int resize_stripes(struct r5conf *conf, int newsize) err = -ENOMEM; mutex_unlock(&conf->cache_size_mutex); + + conf->slab_cache = sc; + conf->active_name = 1-conf->active_name; + /* Step 4, return new stripes to service */ while(!list_empty(&newstripes)) { nsh = list_entry(newstripes.next, struct stripe_head, lru); @@ -2249,8 +2253,6 @@ static int resize_stripes(struct r5conf *conf, int newsize) } /* critical section pass, GFP_NOIO no longer needed */ - conf->slab_cache = sc; - conf->active_name = 1-conf->active_name; if (!err) conf->pool_size = newsize; return err; diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 0b219a81e8a2..c1b999c6cff2 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -99,6 +99,10 @@ config MEDIA_CEC_DEBUG config MEDIA_CEC_EDID bool +config MEDIA_CEC_NOTIFIER + bool + select MEDIA_CEC_EDID + # # Media controller # Selectable only for webcam/grabbers, as other drivers don't use it diff --git a/drivers/media/Makefile b/drivers/media/Makefile index ba516dcbc6aa..6c7801919b98 100644 --- a/drivers/media/Makefile +++ b/drivers/media/Makefile @@ -10,6 +10,10 @@ ifeq ($(CONFIG_MEDIA_CEC_SUPPORT),y) obj-$(CONFIG_MEDIA_SUPPORT) += cec/ endif +ifeq ($(CONFIG_MEDIA_CEC_NOTIFIER),y) + obj-$(CONFIG_MEDIA_SUPPORT) += cec-notifier.o +endif + media-objs := media-device.o media-devnode.o media-entity.o # diff --git a/drivers/media/cec-notifier.c b/drivers/media/cec-notifier.c new file mode 100644 index 000000000000..5f5209a73665 --- /dev/null +++ b/drivers/media/cec-notifier.c @@ -0,0 +1,129 @@ +/* + * cec-notifier.c - notify CEC drivers of physical address changes + * + * Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk> + * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may 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. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/export.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/kref.h> + +#include <media/cec-notifier.h> +#include <drm/drm_edid.h> + +struct cec_notifier { + struct mutex lock; + struct list_head head; + struct kref kref; + struct device *dev; + struct cec_adapter *cec_adap; + void (*callback)(struct cec_adapter *adap, u16 pa); + + u16 phys_addr; +}; + +static LIST_HEAD(cec_notifiers); +static DEFINE_MUTEX(cec_notifiers_lock); + +struct cec_notifier *cec_notifier_get(struct device *dev) +{ + struct cec_notifier *n; + + mutex_lock(&cec_notifiers_lock); + list_for_each_entry(n, &cec_notifiers, head) { + if (n->dev == dev) { + kref_get(&n->kref); + mutex_unlock(&cec_notifiers_lock); + return n; + } + } + n = kzalloc(sizeof(*n), GFP_KERNEL); + if (!n) + goto unlock; + n->dev = dev; + n->phys_addr = CEC_PHYS_ADDR_INVALID; + mutex_init(&n->lock); + kref_init(&n->kref); + list_add_tail(&n->head, &cec_notifiers); +unlock: + mutex_unlock(&cec_notifiers_lock); + return n; +} +EXPORT_SYMBOL_GPL(cec_notifier_get); + +static void cec_notifier_release(struct kref *kref) +{ + struct cec_notifier *n = + container_of(kref, struct cec_notifier, kref); + + list_del(&n->head); + kfree(n); +} + +void cec_notifier_put(struct cec_notifier *n) +{ + mutex_lock(&cec_notifiers_lock); + kref_put(&n->kref, cec_notifier_release); + mutex_unlock(&cec_notifiers_lock); +} +EXPORT_SYMBOL_GPL(cec_notifier_put); + +void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa) +{ + mutex_lock(&n->lock); + n->phys_addr = pa; + if (n->callback) + n->callback(n->cec_adap, n->phys_addr); + mutex_unlock(&n->lock); +} +EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr); + +void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n, + const struct edid *edid) +{ + u16 pa = CEC_PHYS_ADDR_INVALID; + + if (edid && edid->extensions) + pa = cec_get_edid_phys_addr((const u8 *)edid, + EDID_LENGTH * (edid->extensions + 1), NULL); + cec_notifier_set_phys_addr(n, pa); +} +EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid); + +void cec_notifier_register(struct cec_notifier *n, + struct cec_adapter *adap, + void (*callback)(struct cec_adapter *adap, u16 pa)) +{ + kref_get(&n->kref); + mutex_lock(&n->lock); + n->cec_adap = adap; + n->callback = callback; + n->callback(adap, n->phys_addr); + mutex_unlock(&n->lock); +} +EXPORT_SYMBOL_GPL(cec_notifier_register); + +void cec_notifier_unregister(struct cec_notifier *n) +{ + mutex_lock(&n->lock); + n->callback = NULL; + mutex_unlock(&n->lock); + cec_notifier_put(n); +} +EXPORT_SYMBOL_GPL(cec_notifier_unregister); diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c index aca3ab83a8a1..61adc28ec8ec 100644 --- a/drivers/media/cec/cec-core.c +++ b/drivers/media/cec/cec-core.c @@ -195,6 +195,24 @@ static void cec_devnode_unregister(struct cec_devnode *devnode) put_device(&devnode->dev); } +#ifdef CONFIG_MEDIA_CEC_NOTIFIER +static void cec_cec_notify(struct cec_adapter *adap, u16 pa) +{ + cec_s_phys_addr(adap, pa, false); +} + +void cec_register_cec_notifier(struct cec_adapter *adap, + struct cec_notifier *notifier) +{ + if (WARN_ON(!adap->devnode.registered)) + return; + + adap->notifier = notifier; + cec_notifier_register(adap->notifier, adap, cec_cec_notify); +} +EXPORT_SYMBOL_GPL(cec_register_cec_notifier); +#endif + struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, void *priv, const char *name, u32 caps, u8 available_las) @@ -344,6 +362,10 @@ void cec_unregister_adapter(struct cec_adapter *adap) adap->rc = NULL; #endif debugfs_remove_recursive(adap->cec_dir); +#ifdef CONFIG_MEDIA_CEC_NOTIFIER + if (adap->notifier) + cec_notifier_unregister(adap->notifier); +#endif cec_devnode_unregister(&adap->devnode); } EXPORT_SYMBOL_GPL(cec_unregister_adapter); diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index fdffb2f0ded8..107853b0fddd 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -2678,7 +2678,9 @@ static struct dvb_frontend_ops cxd2841er_dvbt_t2_ops = { FE_CAN_MUTE_TS | FE_CAN_2G_MODULATION, .frequency_min = 42000000, - .frequency_max = 1002000000 + .frequency_max = 1002000000, + .symbol_rate_min = 870000, + .symbol_rate_max = 11700000 }, .init = cxd2841er_init_tc, .sleep = cxd2841er_sleep_tc, diff --git a/drivers/media/i2c/adv7481.c b/drivers/media/i2c/adv7481.c index 359a860fdabb..b382a1d83d92 100644 --- a/drivers/media/i2c/adv7481.c +++ b/drivers/media/i2c/adv7481.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,29 +27,68 @@ #include <media/v4l2-ctrls.h> #include <linux/mutex.h> #include <linux/delay.h> + +#include <linux/platform_device.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> + #include <media/adv7481.h> #include <media/msm_ba.h> #include "adv7481_reg.h" +#include "msm_cci.h" +#include "msm_camera_i2c.h" +#include "msm_camera_io_util.h" +#include "msm_camera_dt_util.h" + #define DRIVER_NAME "adv7481" -#define I2C_RW_DELAY 100 -#define I2C_SW_RST_DELAY 10000 -#define GPIO_HW_DELAY_LOW 100000 -#define GPIO_HW_DELAY_HI 10000 +#define I2C_RW_DELAY 1 +#define I2C_SW_RST_DELAY 5000 +#define GPIO_HW_RST_DELAY_HI 10000 +#define GPIO_HW_RST_DELAY_LOW 10000 #define SDP_MIN_SLEEP 5000 #define SDP_MAX_SLEEP 6000 #define SDP_NUM_TRIES 30 #define LOCK_MIN_SLEEP 5000 #define LOCK_MAX_SLEEP 6000 -#define LOCK_NUM_TRIES 20 +#define LOCK_NUM_TRIES 200 + +#define MAX_DEFAULT_WIDTH 1280 +#define MAX_DEFAULT_HEIGHT 720 +#define MAX_DEFAULT_FRAME_RATE 60 +#define MAX_DEFAULT_PIX_CLK_HZ 74240000 #define ONE_MHZ_TO_HZ 1000000 +#define I2C_BLOCK_WRITE_SIZE 1024 + +enum adv7481_gpio_t { + + CCI_I2C_SDA = 0, + CCI_I2C_SCL, + + ADV7481_GPIO_RST, + + ADV7481_GPIO_INT1, + ADV7481_GPIO_INT2, + ADV7481_GPIO_INT3, + + ADV7481_GPIO_MAX, +}; struct adv7481_state { - /* Platform Data */ - struct adv7481_platform_data pdata; + struct device *dev; + + /* VREG */ + struct camera_vreg_t *cci_vreg; + struct regulator *cci_reg_ptr[MAX_REGULATOR]; + int32_t regulator_count; + + /* I2C */ + struct msm_camera_i2c_client i2c_client; + u32 cci_master; + u32 i2c_slave_addr; /* V4L2 Data */ struct v4l2_subdev sd; @@ -63,19 +102,25 @@ struct adv7481_state { struct workqueue_struct *work_queues; struct mutex mutex; - struct i2c_client *client; - struct i2c_client *i2c_csi_txa; - struct i2c_client *i2c_csi_txb; - struct i2c_client *i2c_hdmi; - struct i2c_client *i2c_edid; - struct i2c_client *i2c_cp; - struct i2c_client *i2c_sdp; - struct i2c_client *i2c_rep; + uint8_t i2c_io_addr; + uint8_t i2c_csi_txa_addr; + uint8_t i2c_csi_txb_addr; + uint8_t i2c_hdmi_addr; + uint8_t i2c_edid_addr; + uint8_t i2c_cp_addr; + uint8_t i2c_sdp_addr; + uint8_t i2c_rep_addr; + uint8_t i2c_cbus_addr; /* device status and Flags */ int irq; int device_num; int powerup; + int cec_detected; + int clocks_requested; + + /* GPIOs */ + struct gpio gpio_array[ADV7481_GPIO_MAX]; /* routing configuration data */ int csia_src; @@ -196,6 +241,9 @@ const uint8_t adv7481_default_edid_data[] = { #define ADV7481_EDID_SIZE ARRAY_SIZE(adv7481_default_edid_data) +static u32 adv7481_inp_to_ba(u32 adv_input); +static bool adv7481_is_timing_locked(struct adv7481_state *state); + static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) { return &(container_of(ctrl->handler, @@ -208,56 +256,151 @@ static inline struct adv7481_state *to_state(struct v4l2_subdev *sd) } /* I2C Rd/Rw Functions */ -static int adv7481_wr_byte(struct i2c_client *i2c_client, unsigned int reg, - unsigned int value) +static int32_t adv7481_cci_i2c_write(struct msm_camera_i2c_client *i2c_client, + uint8_t reg, uint16_t *data, + enum msm_camera_i2c_data_type data_type) { - int ret; + return i2c_client->i2c_func_tbl->i2c_write(i2c_client, reg, + *data, data_type); +} - ret = i2c_smbus_write_byte_data(i2c_client, reg & 0xFF, value); - usleep_range(I2C_RW_DELAY, 2*I2C_RW_DELAY); +static int32_t adv7481_cci_i2c_write_seq( + struct msm_camera_i2c_client *i2c_client, + uint8_t reg, const uint8_t *data, uint32_t size) +{ + return i2c_client->i2c_func_tbl->i2c_write_seq(i2c_client, reg, + (uint8_t *)data, size); +} + +static int32_t adv7481_cci_i2c_read(struct msm_camera_i2c_client *i2c_client, + uint8_t reg, uint16_t *data, + enum msm_camera_i2c_data_type data_type) +{ + return i2c_client->i2c_func_tbl->i2c_read(i2c_client, reg, + data, data_type); +} + +static int32_t adv7481_wr_byte(struct msm_camera_i2c_client *c_i2c_client, + uint8_t sid, uint8_t reg, uint8_t data) +{ + uint16_t write_data = data; + int ret = 0; + + c_i2c_client->cci_client->sid = sid; + + ret = adv7481_cci_i2c_write(c_i2c_client, reg, &write_data, + MSM_CAMERA_I2C_BYTE_DATA); + if (ret < 0) + pr_err("Error %d writing cci i2c\n", ret); return ret; } -static int adv7481_rd_byte(struct i2c_client *i2c_client, unsigned int reg) +static int32_t adv7481_wr_block(struct msm_camera_i2c_client *c_i2c_client, + uint8_t sid, uint8_t reg, const uint8_t *data, uint32_t size) { - int ret; + int ret = 0; - ret = i2c_smbus_read_byte_data(i2c_client, reg & 0xFF); - usleep_range(I2C_RW_DELAY, 2*I2C_RW_DELAY); + c_i2c_client->cci_client->sid = sid; + + ret = adv7481_cci_i2c_write_seq(c_i2c_client, reg, data, size); + if (ret < 0) + pr_err("Error %d writing cci i2c block data\n", ret); return ret; } +static uint8_t adv7481_rd_byte(struct msm_camera_i2c_client *c_i2c_client, + uint8_t sid, uint8_t reg) +{ + uint16_t data = 0; + int ret = 0; + + c_i2c_client->cci_client->sid = sid; + ret = adv7481_cci_i2c_read(c_i2c_client, reg, &data, + MSM_CAMERA_I2C_BYTE_DATA); + if (ret < 0) { + pr_err("Error %d reading cci i2c\n", ret); + return ret; + } + + return (uint8_t)(data & 0xFF); +} + +static uint16_t adv7481_rd_word(struct msm_camera_i2c_client *c_i2c_client, + uint8_t sid, uint8_t reg) +{ + uint16_t data = 0; + int ret; + + c_i2c_client->cci_client->sid = sid; + ret = adv7481_cci_i2c_read(c_i2c_client, reg, &data, + MSM_CAMERA_I2C_WORD_DATA); + if (ret < 0) { + pr_err("Error %d reading cci i2c\n", ret); + return ret; + } + + return data; +} + static int adv7481_set_irq(struct adv7481_state *state) { int ret = 0; - ret = adv7481_wr_byte(state->client, IO_REG_PAD_CTRL_1_ADDR, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_PAD_CTRL_1_ADDR, ADV_REG_SETFIELD(1, IO_PDN_INT2) | ADV_REG_SETFIELD(1, IO_PDN_INT3) | ADV_REG_SETFIELD(1, IO_INV_LLC) | ADV_REG_SETFIELD(AD_MID_DRIVE_STRNGTH, IO_DRV_LLC_PAD)); - ret |= adv7481_wr_byte(state->client, IO_REG_INT1_CONF_ADDR, + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_INT1_CONF_ADDR, ADV_REG_SETFIELD(AD_ACTIVE_UNTIL_CLR, IO_INTRQ_DUR_SEL) | ADV_REG_SETFIELD(AD_OP_DRIVE_LOW, IO_INTRQ_OP_SEL)); - ret |= adv7481_wr_byte(state->client, IO_REG_INT2_CONF_ADDR, + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_INT2_CONF_ADDR, ADV_REG_SETFIELD(1, IO_CP_LOCK_UNLOCK_EDGE_SEL)); - ret |= adv7481_wr_byte(state->client, IO_REG_DATAPATH_INT_MASKB_ADDR, + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_DATAPATH_INT_MASKB_ADDR, ADV_REG_SETFIELD(1, IO_CP_LOCK_CP_MB1) | ADV_REG_SETFIELD(1, IO_CP_UNLOCK_CP_MB1) | ADV_REG_SETFIELD(1, IO_VMUTE_REQUEST_HDMI_MB1) | ADV_REG_SETFIELD(1, IO_INT_SD_MB1)); - /* Set hpa */ - ret |= adv7481_wr_byte(state->client, IO_HDMI_LVL_INT_MASKB_3_ADDR, - ADV_REG_SETFIELD(1, IO_CABLE_DET_A_MB1)); + /* Set cable detect */ + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_HDMI_LVL_INT_MASKB_3_ADDR, + ADV_REG_SETFIELD(1, IO_CABLE_DET_A_MB1) | + ADV_REG_SETFIELD(1, IO_V_LOCKED_MB1) | + ADV_REG_SETFIELD(1, IO_DE_REGEN_LCK_MB1)); + + /* set CVBS lock/unlock interrupts */ + /* Select SDP MAP 1 */ + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x20); + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_LOCK_UNLOCK_MASK_ADDR, 0x03); + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); if (ret) pr_err("%s: Failed %d to setup interrupt regs\n", __func__, ret); - else - enable_irq(state->irq); + + return ret; +} + +static int adv7481_reset_irq(struct adv7481_state *state) +{ + int ret = 0; + + disable_irq(state->irq); + + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_DATAPATH_INT_MASKB_ADDR, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_HDMI_LVL_INT_MASKB_3_ADDR, 0x00); return ret; } @@ -267,27 +410,42 @@ static int adv7481_set_edid(struct adv7481_state *state) int i; int ret = 0; uint8_t edid_state; + uint32_t data_left = 0; + uint32_t start_pos; /* Enable Manual Control of EDID on Port A */ - ret |= adv7481_wr_byte(state->i2c_rep, 0x74, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, 0x74, + 0x01); /* Disable Auto Enable of EDID */ - ret |= adv7481_wr_byte(state->i2c_rep, 0x7A, 0x08); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, 0x7A, + 0x08); /* Set Primary EDID Size to 256 Bytes */ - ret |= adv7481_wr_byte(state->i2c_rep, 0x70, 0x20); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, 0x70, + 0x20); /* * Readback EDID enable state after a combination of manual * and automatic functions */ - edid_state = adv7481_rd_byte(state->i2c_rep, + edid_state = adv7481_rd_byte(&state->i2c_client, state->i2c_rep_addr, HDMI_REG_RO_EDID_DEBUG_2_ADDR); pr_debug("%s: Readback EDID enable state: 0x%x\n", __func__, edid_state); - for (i = 0; i < ADV7481_EDID_SIZE; i++) { - ret |= adv7481_wr_byte(state->i2c_edid, i, - adv7481_default_edid_data[i]); - } + for (i = 0; i < ADV7481_EDID_SIZE && !ret; i += I2C_BLOCK_WRITE_SIZE) + ret |= adv7481_wr_block(&state->i2c_client, + state->i2c_edid_addr, + i, &adv7481_default_edid_data[i], + I2C_BLOCK_WRITE_SIZE); + + data_left = ADV7481_EDID_SIZE % I2C_BLOCK_WRITE_SIZE; + start_pos = ADV7481_EDID_SIZE - data_left; + if (data_left && !ret) + ret |= adv7481_wr_block(&state->i2c_client, + state->i2c_edid_addr, + start_pos, + &adv7481_default_edid_data[start_pos], + data_left); return ret; } @@ -301,47 +459,232 @@ static irqreturn_t adv7481_irq(int irq, void *dev) return IRQ_HANDLED; } +/* Request CCI clocks for adv7481 register access */ +static int adv7481_request_cci_clks(struct adv7481_state *state) +{ + int ret = 0; + + if (state->clocks_requested == TRUE) + return ret; + + ret = state->i2c_client.i2c_func_tbl->i2c_util( + &state->i2c_client, MSM_CCI_INIT); + if (ret < 0) + pr_err("%s - cci_init failed\n", __func__); + else + state->clocks_requested = TRUE; + + /* enable camera voltage regulator */ + ret = msm_camera_enable_vreg(state->dev, state->cci_vreg, + state->regulator_count, NULL, 0, + &state->cci_reg_ptr[0], 1); + if (ret < 0) + pr_err("%s:cci enable_vreg failed\n", __func__); + else + pr_debug("%s - VREG Initialized...\n", __func__); + + return ret; +} + +static int adv7481_release_cci_clks(struct adv7481_state *state) +{ + int ret = 0; + + if (state->clocks_requested == FALSE) + return ret; + + ret = state->i2c_client.i2c_func_tbl->i2c_util( + &state->i2c_client, MSM_CCI_RELEASE); + if (ret < 0) + pr_err("%s - cci_release failed\n", __func__); + else + state->clocks_requested = FALSE; + + /* disable camera voltage regulator */ + ret = msm_camera_enable_vreg(state->dev, state->cci_vreg, + state->regulator_count, NULL, 0, + &state->cci_reg_ptr[0], 0); + if (ret < 0) + pr_err("%s:cci disable vreg failed\n", __func__); + else + pr_debug("%s - VREG Initialized...\n", __func__); + + return ret; +} + static void adv7481_irq_delay_work(struct work_struct *work) { struct adv7481_state *state; - uint8_t status; + uint8_t int_raw_status; + uint8_t int_status; + uint8_t raw_status; state = container_of(work, struct adv7481_state, irq_delayed_work.work); mutex_lock(&state->mutex); - /* workaround for irq trigger */ - status = adv7481_rd_byte(state->client, + /* Read raw irq status register */ + int_raw_status = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_INT_RAW_STATUS_ADDR); pr_debug("%s: dev: %d got int raw status: 0x%x\n", __func__, - state->device_num, status); + state->device_num, int_raw_status); + state->cec_detected = ADV_REG_GETFIELD(int_raw_status, IO_INT_CEC_ST); + + while (int_raw_status) { + if (ADV_REG_GETFIELD(int_raw_status, IO_INTRQ1_RAW)) { + int lock_status = -1; + struct v4l2_event event = {0}; + int *ptr = (int *)event.u.data; + + pr_debug("%s: dev: %d got intrq1_raw\n", __func__, + state->device_num); + int_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_REG_DATAPATH_INT_STATUS_ADDR); + + raw_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_REG_DATAPATH_RAW_STATUS_ADDR); + + adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_DATAPATH_INT_CLEAR_ADDR, int_status); + + pr_debug("%s: dev: %d got datapath int status: 0x%x\n", + __func__, state->device_num, int_status); + + pr_debug("%s: dev: %d got datapath raw status: 0x%x\n", + __func__, state->device_num, raw_status); + + if (ADV_REG_GETFIELD(int_status, IO_INT_SD_ST) && + ADV_REG_GETFIELD(raw_status, IO_INT_SD_RAW)) { + uint8_t sdp_sts = 0; + + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RW_MAP_REG, + 0x01); + sdp_sts = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, + SDP_RO_MAIN_STATUS1_ADDR); + pr_debug("%s: dev: %d got sdp status: 0x%x\n", + __func__, state->device_num, sdp_sts); + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RW_MAP_REG, + 0x00); + if (ADV_REG_GETFIELD(sdp_sts, + SDP_RO_MAIN_IN_LOCK)) { + lock_status = 0; + pr_debug( + "%s: set lock_status SDP_IN_LOCK:0x%x\n", + __func__, lock_status); + } else { + lock_status = 1; + pr_debug( + "%s: set lock_status SDP_UNLOCK:0x%x\n", + __func__, lock_status); + } + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RW_MAP_REG, + 0x20); + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, + SDP_RW_LOCK_UNLOCK_CLR_ADDR, sdp_sts); + adv7481_wr_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RW_MAP_REG, + 0x00); + } else { + if (ADV_REG_GETFIELD(int_status, + IO_CP_LOCK_CP_ST) && + ADV_REG_GETFIELD(raw_status, + IO_CP_LOCK_CP_RAW)) { + lock_status = 0; + pr_debug( + "%s: set lock_status IO_CP_LOCK_CP_RAW:0x%x\n", + __func__, lock_status); + } + if (ADV_REG_GETFIELD(int_status, + IO_CP_UNLOCK_CP_ST) && + ADV_REG_GETFIELD(raw_status, + IO_CP_UNLOCK_CP_RAW)) { + lock_status = 1; + pr_debug( + "%s: set lock_status IO_CP_UNLOCK_CP_RAW:0x%x\n", + __func__, lock_status); + } + } + + if (lock_status >= 0) { + ptr[0] = adv7481_inp_to_ba(state->mode); + ptr[1] = lock_status; + event.type = lock_status ? + V4L2_EVENT_MSM_BA_SIGNAL_LOST_LOCK : + V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK; + v4l2_subdev_notify(&state->sd, + event.type, &event); + } + } - status = adv7481_rd_byte(state->client, - IO_REG_DATAPATH_INT_STATUS_ADDR); + if (ADV_REG_GETFIELD(int_raw_status, IO_INT_HDMI_ST)) { + int cable_detected = 0; + struct v4l2_event event = {0}; + int *ptr = (int *)event.u.data; - pr_debug("%s: dev: %d got datapath int status: 0x%x\n", __func__, - state->device_num, status); + ptr[0] = adv7481_inp_to_ba(state->mode); - adv7481_wr_byte(state->client, - IO_REG_DATAPATH_INT_CLEAR_ADDR, status); + pr_debug("%s: dev: %d got int_hdmi_st\n", __func__, + state->device_num); - status = adv7481_rd_byte(state->client, - IO_REG_DATAPATH_RAW_STATUS_ADDR); + int_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_HDMI_LVL_INT_STATUS_3_ADDR); - pr_debug("%s: dev: %d got datapath rawstatus: 0x%x\n", __func__, - state->device_num, status); + raw_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_HDMI_LVL_RAW_STATUS_3_ADDR); - status = adv7481_rd_byte(state->client, - IO_HDMI_LVL_INT_STATUS_3_ADDR); + adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_HDMI_LVL_INT_CLEAR_3_ADDR, int_status); - pr_debug("%s: dev: %d got hdmi lvl int status 3: 0x%x\n", __func__, - state->device_num, status); + pr_debug("%s: dev: %d got hdmi lvl int status 3: 0x%x\n", + __func__, state->device_num, int_status); + pr_debug("%s: dev: %d got hdmi lvl raw status 3: 0x%x\n", + __func__, state->device_num, raw_status); - adv7481_wr_byte(state->client, - IO_HDMI_LVL_INT_CLEAR_3_ADDR, status); + if (ADV_REG_GETFIELD(int_status, IO_CABLE_DET_A_ST)) { + cable_detected = ADV_REG_GETFIELD(raw_status, + IO_CABLE_DET_A_RAW); + pr_debug("%s: set cable_detected: 0x%x\n", + __func__, cable_detected); + ptr[1] = cable_detected; + event.type = V4L2_EVENT_MSM_BA_CABLE_DETECT; + v4l2_subdev_notify(&state->sd, + event.type, &event); + } + /* Assumption is that vertical sync int + * is the last one to come + */ + if (ADV_REG_GETFIELD(int_status, IO_V_LOCKED_ST)) { + if (ADV_REG_GETFIELD(raw_status, + IO_TMDSPLL_LCK_A_RAW) && + ADV_REG_GETFIELD(raw_status, + IO_V_LOCKED_RAW) && + ADV_REG_GETFIELD(raw_status, + IO_DE_REGEN_LCK_RAW)) { + pr_debug("%s: port settings changed\n", + __func__); + event.type = + V4L2_EVENT_MSM_BA_PORT_SETTINGS_CHANGED; + v4l2_subdev_notify(&state->sd, + event.type, &event); + } + } + } + int_raw_status = adv7481_rd_byte(&state->i2c_client, + state->i2c_io_addr, + IO_REG_INT_RAW_STATUS_ADDR); + } mutex_unlock(&state->mutex); } @@ -350,110 +693,100 @@ static int adv7481_cec_wakeup(struct adv7481_state *state, bool enable) uint8_t val; int ret = 0; - val = adv7481_rd_byte(state->client, + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_PWR_DN2_XTAL_HIGH_ADDR); val = ADV_REG_GETFIELD(val, IO_PROG_XTAL_FREQ_HIGH); if (enable) { /* CEC wake up enabled in power-down mode */ val |= ADV_REG_SETFIELD(1, IO_CTRL_CEC_WAKE_UP_PWRDN2B) | ADV_REG_SETFIELD(0, IO_CTRL_CEC_WAKE_UP_PWRDNB); - ret = adv7481_wr_byte(state->client, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_PWR_DN2_XTAL_HIGH_ADDR, val); } else { /* CEC wake up disabled in power-down mode */ val |= ADV_REG_SETFIELD(0, IO_CTRL_CEC_WAKE_UP_PWRDN2B) | ADV_REG_SETFIELD(1, IO_CTRL_CEC_WAKE_UP_PWRDNB); - ret = adv7481_wr_byte(state->client, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_PWR_DN2_XTAL_HIGH_ADDR, val); } return ret; } /* Initialize adv7481 I2C Settings */ -static int adv7481_dev_init(struct adv7481_state *state, - struct i2c_client *client) +static int adv7481_dev_init(struct adv7481_state *state) { + uint16_t chip_rev_id; int ret; mutex_lock(&state->mutex); /* Soft reset */ - ret = adv7481_wr_byte(state->client, - IO_REG_MAIN_RST_ADDR, IO_REG_MAIN_RST_VALUE); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_MAIN_RST_ADDR, IO_REG_MAIN_RST_VALUE); /* Delay required following I2C reset and I2C transactions */ - usleep_range(I2C_SW_RST_DELAY, I2C_SW_RST_DELAY+1000); + udelay(I2C_SW_RST_DELAY); + + chip_rev_id = adv7481_rd_word(&state->i2c_client, state->i2c_io_addr, + IO_REG_CHIP_REV_ID_1_ADDR); + pr_debug("%s: ADV7481 chip rev id: 0x%x", __func__, chip_rev_id); /* Disable CEC wake up in power-down mode */ ret |= adv7481_cec_wakeup(state, 0); /* Setting Vid_Std to 720x480p60 */ - ret |= adv7481_wr_byte(state->client, - IO_REG_CP_VID_STD_ADDR, 0x4A); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CP_VID_STD_ADDR, 0x4A); /* Configure I2C Maps and I2C Communication Settings */ /* io_reg_f2 I2C Auto Increment */ - ret |= adv7481_wr_byte(state->client, IO_REG_I2C_CFG_ADDR, - IO_REG_I2C_AUTOINC_EN_REG_VALUE); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_I2C_CFG_ADDR, IO_REG_I2C_AUTOINC_EN_REG_VALUE); /* DPLL Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_DPLL_ADDR, - IO_REG_DPLL_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_DPLL_ADDR, IO_REG_DPLL_SADDR); /* CP Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CP_ADDR, - IO_REG_CP_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CP_ADDR, IO_REG_CP_SADDR); /* HDMI RX Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_ADDR, - IO_REG_HDMI_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_HDMI_ADDR, IO_REG_HDMI_SADDR); /* EDID Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_EDID_ADDR, - IO_REG_EDID_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_EDID_ADDR, IO_REG_EDID_SADDR); /* HDMI RX Repeater Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_REP_ADDR, - IO_REG_HDMI_REP_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_HDMI_REP_ADDR, IO_REG_HDMI_REP_SADDR); /* HDMI RX Info-frame Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_HDMI_INF_ADDR, - IO_REG_HDMI_INF_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_HDMI_INF_ADDR, IO_REG_HDMI_INF_SADDR); /* CBUS Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CBUS_ADDR, - IO_REG_CBUS_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CBUS_ADDR, IO_REG_CBUS_SADDR); /* CEC Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CEC_ADDR, - IO_REG_CEC_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CEC_ADDR, IO_REG_CEC_SADDR); /* SDP Main Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_SDP_ADDR, - IO_REG_SDP_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_SDP_ADDR, IO_REG_SDP_SADDR); /* CSI-TXB Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CSI_TXB_ADDR, - IO_REG_CSI_TXB_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_TXB_ADDR, IO_REG_CSI_TXB_SADDR); /* CSI-TXA Map Address */ - ret |= adv7481_wr_byte(state->client, IO_REG_CSI_TXA_ADDR, - IO_REG_CSI_TXA_SADDR); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_TXA_ADDR, IO_REG_CSI_TXA_SADDR); if (ret) { pr_err("%s: Failed dev init %d\n", __func__, ret); goto err_exit; } /* Configure i2c clients */ - state->i2c_csi_txa = i2c_new_dummy(client->adapter, - IO_REG_CSI_TXA_SADDR >> 1); - state->i2c_csi_txb = i2c_new_dummy(client->adapter, - IO_REG_CSI_TXB_SADDR >> 1); - state->i2c_cp = i2c_new_dummy(client->adapter, - IO_REG_CP_SADDR >> 1); - state->i2c_hdmi = i2c_new_dummy(client->adapter, - IO_REG_HDMI_SADDR >> 1); - state->i2c_edid = i2c_new_dummy(client->adapter, - IO_REG_EDID_SADDR >> 1); - state->i2c_sdp = i2c_new_dummy(client->adapter, - IO_REG_SDP_SADDR >> 1); - state->i2c_rep = i2c_new_dummy(client->adapter, - IO_REG_HDMI_REP_SADDR >> 1); - - if (!state->i2c_csi_txa || !state->i2c_csi_txb || !state->i2c_cp || - !state->i2c_sdp || !state->i2c_hdmi || !state->i2c_edid || - !state->i2c_rep) { - pr_err("%s: Additional I2C Client Fail\n", __func__); - ret = -EFAULT; - goto err_exit; - } + state->i2c_csi_txa_addr = IO_REG_CSI_TXA_SADDR >> 1; + state->i2c_csi_txb_addr = IO_REG_CSI_TXB_SADDR >> 1; + state->i2c_cp_addr = IO_REG_CP_SADDR >> 1; + state->i2c_hdmi_addr = IO_REG_HDMI_SADDR >> 1; + state->i2c_edid_addr = IO_REG_EDID_SADDR >> 1; + state->i2c_sdp_addr = IO_REG_SDP_SADDR >> 1; + state->i2c_rep_addr = IO_REG_HDMI_REP_SADDR >> 1; + state->i2c_cbus_addr = IO_REG_CBUS_SADDR >> 1; ret = adv7481_set_edid(state); ret |= adv7481_set_irq(state); @@ -465,28 +798,25 @@ err_exit: } /* Initialize adv7481 hardware */ -static int adv7481_hw_init(struct adv7481_platform_data *pdata, - struct adv7481_state *state) +static int adv7481_hw_init(struct adv7481_state *state) { int ret = 0; - if (!pdata) { - pr_err("%s: PDATA is NULL\n", __func__); - return -EFAULT; - } - mutex_lock(&state->mutex); - if (gpio_is_valid(pdata->rstb_gpio)) { - ret = gpio_request(pdata->rstb_gpio, "rstb_gpio"); - if (ret) { - pr_err("%s: Request GPIO Fail %d\n", __func__, ret); - goto err_exit; - } - ret = gpio_direction_output(pdata->rstb_gpio, 0); - usleep_range(GPIO_HW_DELAY_LOW, GPIO_HW_DELAY_LOW+1000); - ret = gpio_direction_output(pdata->rstb_gpio, 1); - usleep_range(GPIO_HW_DELAY_HI, GPIO_HW_DELAY_HI+1000); + /* Bring ADV7481 out of reset */ + ret = gpio_request_array(&state->gpio_array[ADV7481_GPIO_RST], 1); + if (ret < 0) { + pr_err("%s: Failed to request reset GPIO %d\n", __func__, ret); + goto err_exit; + } + if (gpio_is_valid(state->gpio_array[ADV7481_GPIO_RST].gpio)) { + ret |= gpio_direction_output( + state->gpio_array[ADV7481_GPIO_RST].gpio, 0); + udelay(GPIO_HW_RST_DELAY_LOW); + ret |= gpio_direction_output( + state->gpio_array[ADV7481_GPIO_RST].gpio, 1); + udelay(GPIO_HW_RST_DELAY_HI); if (ret) { pr_err("%s: Set GPIO Fail %d\n", __func__, ret); goto err_exit; @@ -494,22 +824,21 @@ static int adv7481_hw_init(struct adv7481_platform_data *pdata, } /* Only setup IRQ1 for now... */ - if (gpio_is_valid(pdata->irq1_gpio)) { - ret = gpio_request(pdata->irq1_gpio, "irq_gpio"); - if (ret) { - pr_err("%s: Failed to request irq_gpio %d\n", - __func__, ret); - goto err_exit; - } - - ret = gpio_direction_input(pdata->irq1_gpio); + ret = gpio_request_array(&state->gpio_array[ADV7481_GPIO_INT1], 1); + if (ret < 0) { + pr_err("%s: Failed to request irq_gpio %d\n", __func__, ret); + goto err_exit; + } + if (gpio_is_valid(state->gpio_array[ADV7481_GPIO_INT1].gpio)) { + ret |= gpio_direction_input( + state->gpio_array[ADV7481_GPIO_INT1].gpio); if (ret) { pr_err("%s: Failed gpio_direction irq %d\n", __func__, ret); goto err_exit; } - - state->irq = gpio_to_irq(pdata->irq1_gpio); + state->irq = gpio_to_irq( + state->gpio_array[ADV7481_GPIO_INT1].gpio); if (state->irq) { ret = request_irq(state->irq, adv7481_irq, IRQF_ONESHOT | IRQF_TRIGGER_FALLING, @@ -519,18 +848,16 @@ static int adv7481_hw_init(struct adv7481_platform_data *pdata, __func__, ret); goto err_exit; } + /* disable irq until chip interrupts are programmed */ + disable_irq(state->irq); } else { pr_err("%s: Failed gpio_to_irq %d\n", __func__, ret); ret = -EINVAL; goto err_exit; } - - /* disable irq until chip interrupts are programmed */ - disable_irq(state->irq); - - INIT_DELAYED_WORK(&state->irq_delayed_work, - adv7481_irq_delay_work); } + INIT_DELAYED_WORK(&state->irq_delayed_work, + adv7481_irq_delay_work); err_exit: mutex_unlock(&state->mutex); @@ -548,31 +875,40 @@ static int adv7481_s_ctrl(struct v4l2_ctrl *ctrl) pr_debug("Enter %s: id = 0x%x\n", __func__, ctrl->id); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ); temp |= CP_CTR_VID_ADJ_EN; - ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); - ret |= adv7481_wr_byte(state->client, - CP_REG_BRIGHTNESS, ctrl->val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_BRIGHTNESS, ctrl->val); break; case V4L2_CID_CONTRAST: - temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ); temp |= CP_CTR_VID_ADJ_EN; - ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); - ret |= adv7481_wr_byte(state->client, - CP_REG_CONTRAST, ctrl->val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_CONTRAST, ctrl->val); break; case V4L2_CID_SATURATION: - temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ); temp |= CP_CTR_VID_ADJ_EN; - ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); - ret |= adv7481_wr_byte(state->client, - CP_REG_SATURATION, ctrl->val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_SATURATION, ctrl->val); break; case V4L2_CID_HUE: - temp = adv7481_rd_byte(state->client, CP_REG_VID_ADJ); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ); temp |= CP_CTR_VID_ADJ_EN; - ret = adv7481_wr_byte(state->client, CP_REG_VID_ADJ, temp); - ret |= adv7481_wr_byte(state->client, CP_REG_HUE, ctrl->val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_VID_ADJ, temp); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CP_REG_HUE, ctrl->val); break; default: break; @@ -611,6 +947,108 @@ static int adv7481_s_power(struct v4l2_subdev *sd, int on) return ret; } +static int adv7481_set_cec_logical_addr(struct adv7481_state *state, int *la) +{ + int rc = 0; + uint8_t val; + + if (!la) { + pr_err("%s: NULL pointer provided\n", __func__); + return -EINVAL; + } + + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOG_ADDR_MASK_ADDR); + if (ADV_REG_GETFIELD(val, CEC_REG_LOG_ADDR_MASK0)) { + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS0_1_ADDR); + val = ADV_REG_RSTFIELD(val, CEC_REG_LOGICAL_ADDRESS0); + val |= ADV_REG_SETFIELD(*la, CEC_REG_LOGICAL_ADDRESS0); + rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS0_1_ADDR, val); + } else if (ADV_REG_GETFIELD(val, CEC_REG_LOG_ADDR_MASK1)) { + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS0_1_ADDR); + val = ADV_REG_RSTFIELD(val, CEC_REG_LOGICAL_ADDRESS1); + val |= ADV_REG_SETFIELD(*la, CEC_REG_LOGICAL_ADDRESS1); + rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS0_1_ADDR, val); + } else if (ADV_REG_GETFIELD(val, CEC_REG_LOG_ADDR_MASK2)) { + val = ADV_REG_SETFIELD(*la, CEC_REG_LOGICAL_ADDRESS2); + rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_LOGICAL_ADDRESS2_ADDR, val); + } else { + pr_err("No cec logical address mask set\n"); + } + + return rc; +} + +static int adv7481_cec_powerup(struct adv7481_state *state, int *powerup) +{ + int rc = 0; + uint8_t val = 0; + + if (!powerup) { + pr_err("%s: NULL pointer provided\n", __func__); + return -EINVAL; + } + + pr_debug("%s: set power %d\n", __func__, *powerup); + + val = ADV_REG_SETFIELD(*powerup, CEC_REG_CEC_POWER_UP); + rc = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + CEC_REG_CEC_POWER_UP_ADDR, val); + + return rc; +} + +static long adv7481_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct adv7481_state *state = to_state(sd); + int *ret_val = arg; + long ret = 0; + int param = 0; + + pr_debug("Enter %s with command: 0x%x", __func__, cmd); + + if (!sd) + return -EINVAL; + + switch (cmd) { + case VIDIOC_HDMI_RX_CEC_S_LOGICAL: + ret = adv7481_set_cec_logical_addr(state, arg); + break; + case VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL: + ret = adv7481_set_cec_logical_addr(state, ¶m); + break; + case VIDIOC_HDMI_RX_CEC_G_PHYSICAL: + if (ret_val) { + *ret_val = 0; + } else { + pr_err("%s: NULL pointer provided\n", __func__); + ret = -EINVAL; + } + break; + case VIDIOC_HDMI_RX_CEC_G_CONNECTED: + if (ret_val) { + *ret_val = state->cec_detected; + } else { + pr_err("%s: NULL pointer provided\n", __func__); + ret = -EINVAL; + } + break; + case VIDIOC_HDMI_RX_CEC_S_ENABLE: + ret = adv7481_cec_powerup(state, arg); + break; + default: + pr_err("Not a typewriter! Command: 0x%x", cmd); + ret = -ENOTTY; + break; + } + return ret; +} + static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard) { int ret = 0; @@ -620,18 +1058,22 @@ static int adv7481_get_sd_timings(struct adv7481_state *state, int *sd_standard) if (sd_standard == NULL) return -EINVAL; + /* Select SDP read-only main Map */ + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x01); do { - sdp_stat = adv7481_rd_byte(state->i2c_sdp, - SDP_RO_MAIN_STATUS1_ADDR); + sdp_stat = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR); usleep_range(SDP_MIN_SLEEP, SDP_MAX_SLEEP); timeout++; - sdp_stat2 = adv7481_rd_byte(state->i2c_sdp, - SDP_RO_MAIN_STATUS1_ADDR); + sdp_stat2 = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR); } while ((sdp_stat != sdp_stat2) && (timeout < SDP_NUM_TRIES)); + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); if (sdp_stat != sdp_stat2) { - pr_err("%s(%d), adv7481 SDP status unstable: 1\n", - __func__, __LINE__); + pr_err("%s, adv7481 SDP status unstable: 1\n", __func__); return -ETIMEDOUT; } @@ -681,22 +1123,50 @@ static int adv7481_set_cvbs_mode(struct adv7481_state *state) pr_debug("Enter %s\n", __func__); state->mode = ADV7481_IP_CVBS_1; /* cvbs video settings ntsc etc */ - ret = adv7481_wr_byte(state->client, 0x00, 0x30); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x0f, 0x00); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x00, 0x00); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x03, 0x42); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x04, 0x07); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x13, 0x00); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x17, 0x41); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x31, 0x12); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x52, 0xcd); - ret |= adv7481_wr_byte(state->i2c_sdp, 0x0e, 0xff); - val = adv7481_rd_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x00, 0x30); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x0e, 0xff); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x0f, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x52, 0xcd); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x00, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x80); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x9c, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x9c, 0xff); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x80, 0x51); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x81, 0x51); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x82, 0x68); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x03, 0x42); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x04, 0x07); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x13, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x17, 0x41); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + 0x31, 0x12); + + val = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_PIX_EN_SEL_ADDR); /* Output of SD core routed to MIPI CSI 4-lane Tx */ - val |= ADV_REG_SETFIELD(0x10, IO_CTRL_CSI4_IN_SEL); - ret |= adv7481_wr_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR, val); - /* Enable autodetect */ - ret |= adv7481_wr_byte(state->i2c_sdp, 0x0e, 0x81); + val = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | + ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | + ADV_REG_SETFIELD(0x2, IO_CTRL_CSI4_IN_SEL); + + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_PIX_EN_SEL_ADDR, val); return ret; } @@ -713,69 +1183,101 @@ static int adv7481_set_hdmi_mode(struct adv7481_state *state) * YUV 422 out via TxA CSI: 4-Lane */ /* Disable chip powerdown & Enable HDMI Rx block */ - temp = adv7481_rd_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR); + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_PWR_DOWN_CTRL_ADDR); val = ADV_REG_SETFIELD(1, IO_CTRL_RX_EN) | ADV_REG_SETFIELD(0, IO_CTRL_RX_PWDN) | ADV_REG_SETFIELD(0, IO_CTRL_XTAL_PWDN) | ADV_REG_SETFIELD(0, IO_CTRL_CORE_PWDN) | ADV_REG_SETFIELD(0, IO_CTRL_MASTER_PWDN); - ret = adv7481_wr_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR, val); + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_PWR_DOWN_CTRL_ADDR, val); /* SDR mode */ - ret |= adv7481_wr_byte(state->client, 0x11, 0x48); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x11, 0x48); /* Set CP core to YUV out */ - ret |= adv7481_wr_byte(state->client, 0x04, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x04, 0x00); /* Set CP core to SDR 422 */ - ret |= adv7481_wr_byte(state->client, 0x12, 0xF2); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x12, 0xF2); /* Saturate both Luma and Chroma values to 254 */ - ret |= adv7481_wr_byte(state->client, 0x17, 0x80); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x17, 0x80); /* Set CP core to enable AV codes */ - ret |= adv7481_wr_byte(state->client, 0x03, 0x86); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x03, 0x86); /* ADI RS CP Core: */ - ret |= adv7481_wr_byte(state->i2c_cp, 0x7C, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_cp_addr, + 0x7C, 0x00); /* Set CP core Phase Adjustment */ - ret |= adv7481_wr_byte(state->client, 0x0C, 0xE0); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + 0x0C, 0xE0); /* LLC/PIX/SPI PINS TRISTATED AUD Outputs Enabled */ - ret |= adv7481_wr_byte(state->client, IO_PAD_CTRLS_ADDR, 0xDD); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_PAD_CTRLS_ADDR, 0xDD); /* Enable Tx A CSI 4-Lane & data from CP core */ val = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); - ret |= adv7481_wr_byte(state->client, IO_REG_CSI_PIX_EN_SEL_ADDR, - val); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_CSI_PIX_EN_SEL_ADDR, val); /* start to configure HDMI Rx once io-map is configured */ /* Enable HDCP 1.1 */ - ret |= adv7481_wr_byte(state->i2c_rep, 0x40, 0x83); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_rep_addr, + 0x40, 0x83); /* Foreground Channel = A */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x00, 0x08); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x00, 0x08); /* ADI Required Write */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x98, 0xFF); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x99, 0xA3); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9A, 0x00); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9B, 0x0A); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x9D, 0x40); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0xCB, 0x09); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x98, 0xFF); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x99, 0xA3); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x9A, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x9B, 0x0A); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x9D, 0x40); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0xCB, 0x09); /* ADI RS */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3D, 0x10); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3E, 0x7B); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x3F, 0x5E); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x4E, 0xFE); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x4F, 0x18); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x57, 0xA3); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x58, 0x04); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x85, 0x10); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x3D, 0x10); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x3E, 0x7B); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x3F, 0x5E); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x4E, 0xFE); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x4F, 0x18); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x57, 0xA3); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x58, 0x04); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x85, 0x10); /* Enable All Terminations */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x83, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x83, 0x00); /* ADI RS */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0xA3, 0x01); - ret |= adv7481_wr_byte(state->i2c_hdmi, 0xBE, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0xA3, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0xBE, 0x00); /* HPA Manual Enable */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x6C, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x6C, 0x01); /* HPA Asserted */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0xF8, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0xF8, 0x01); /* Audio Mute Speed Set to Fastest (Smallest Step Size) */ - ret |= adv7481_wr_byte(state->i2c_hdmi, 0x0F, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, + 0x0F, 0x00); return ret; } @@ -860,7 +1362,7 @@ static int adv7481_set_ip_mode(struct adv7481_state *state, int input) } static int adv7481_set_op_src(struct adv7481_state *state, - int output, int input) + int output, int input) { int ret = 0; int temp = 0; @@ -897,10 +1399,11 @@ static int adv7481_set_op_src(struct adv7481_state *state, default: ret = -EINVAL; } - temp = adv7481_rd_byte(state->client, + temp = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_PWR_DOWN_CTRL_ADDR); temp |= val; - adv7481_wr_byte(state->client, IO_REG_PWR_DOWN_CTRL_ADDR, temp); + adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, + IO_REG_PWR_DOWN_CTRL_ADDR, temp); state->csia_src = input; break; case ADV7481_OP_CSIB: @@ -915,11 +1418,11 @@ static int adv7481_set_op_src(struct adv7481_state *state, return ret; } -static u32 ba_inp_to_adv7481(u32 input) +static u32 ba_inp_to_adv7481(u32 ba_input) { u32 adv_input = ADV7481_IP_HDMI; - switch (input) { + switch (ba_input) { case BA_IP_CVBS_0: adv_input = ADV7481_IP_CVBS_1; break; @@ -954,6 +1457,42 @@ static u32 ba_inp_to_adv7481(u32 input) return adv_input; } +static u32 adv7481_inp_to_ba(u32 adv_input) +{ + u32 ba_input = BA_IP_HDMI_1; + + switch (adv_input) { + case ADV7481_IP_CVBS_1: + ba_input = BA_IP_CVBS_0; + break; + case ADV7481_IP_CVBS_2: + ba_input = BA_IP_CVBS_1; + break; + case ADV7481_IP_CVBS_3: + ba_input = BA_IP_CVBS_2; + break; + case ADV7481_IP_CVBS_4: + ba_input = BA_IP_CVBS_3; + break; + case ADV7481_IP_CVBS_5: + ba_input = BA_IP_CVBS_4; + break; + case ADV7481_IP_CVBS_6: + ba_input = BA_IP_CVBS_5; + break; + case ADV7481_IP_HDMI: + ba_input = BA_IP_HDMI_1; + break; + case ADV7481_IP_TTL: + ba_input = BA_IP_TTL; + break; + default: + ba_input = BA_IP_HDMI_1; + break; + } + return ba_input; +} + static int adv7481_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { @@ -971,14 +1510,11 @@ static int adv7481_s_routing(struct v4l2_subdev *sd, u32 input, goto unlock_exit; } - if (state->mode != adv_input) { - ret = adv7481_set_ip_mode(state, adv_input); - if (ret) - pr_err("%s: Set input mode failed: %d\n", - __func__, ret); - else - state->mode = adv_input; - } + ret = adv7481_set_ip_mode(state, adv_input); + if (ret) + pr_err("%s: Set input mode failed: %d\n", __func__, ret); + else + state->mode = adv_input; unlock_exit: mutex_unlock(&state->mutex); @@ -986,6 +1522,27 @@ unlock_exit: return ret; } +static bool adv7481_is_timing_locked(struct adv7481_state *state) +{ + bool ret = false; + int val1 = 0; + int val2 = 0; + + /* Check Timing Lock IO Map Status3:0x71[0] && 0x71[1] && 0x71[7] */ + val1 = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, + IO_HDMI_LVL_RAW_STATUS_3_ADDR); + val2 = adv7481_rd_byte(&state->i2c_client, state->i2c_cp_addr, + CP_REG_STDI_CH_ADDR); + + if (ADV_REG_GETFIELD(val1, IO_DE_REGEN_LCK_RAW) && + ADV_REG_GETFIELD(val1, IO_V_LOCKED_RAW) && + ADV_REG_GETFIELD(val1, IO_TMDSPLL_LCK_A_RAW) && + ADV_REG_GETFIELD(val2, CP_STDI_DVALID_CH1)) + ret = true; + + return ret; +} + static int adv7481_get_hdmi_timings(struct adv7481_state *state, struct adv7481_vid_params *vid_params, struct adv7481_hdmi_params *hdmi_params) @@ -998,13 +1555,15 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, pr_debug("Enter %s\n", __func__); /* Check TMDS PLL Lock and Frequency */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_HDMI_PARAM4_ADDR); + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_HDMI_PARAM4_ADDR); hdmi_params->pll_lock = ADV_REG_GETFIELD(temp1, HDMI_REG_TMDS_PLL_LOCKED); if (hdmi_params->pll_lock) { - temp1 = adv7481_rd_byte(state->i2c_hdmi, - HDMI_REG_TMDS_FREQ_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, + state->i2c_hdmi_addr, HDMI_REG_TMDS_FREQ_ADDR); + temp2 = adv7481_rd_byte(&state->i2c_client, + state->i2c_hdmi_addr, HDMI_REG_TMDS_FREQ_FRAC_ADDR); hdmi_params->tmds_freq = ADV_REG_GETFIELD(temp1, HDMI_REG_TMDS_FREQ); @@ -1015,34 +1574,29 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, hdmi_params->tmds_freq += ADV_REG_GETFIELD(temp2, HDMI_REG_TMDS_FREQ_FRAC)*ONE_MHZ_TO_HZ/128; } else { - pr_err("%s: PLL not locked return EBUSY\n", __func__); - return -EBUSY; + pr_err("%s(%d): PLL not locked return EBUSY\n", + __func__, __LINE__); + ret = -EBUSY; + goto set_default; } - /* Check Timing Lock IO Map Status3:0x71[0] && 0x71[1] && 0x71[7] */ + /* Check Timing Lock */ do { - temp1 = adv7481_rd_byte(state->client, - IO_HDMI_LVL_RAW_STATUS_3_ADDR); - temp2 = adv7481_rd_byte(state->i2c_cp, - CP_REG_STDI_CH_ADDR); - - if (ADV_REG_GETFIELD(temp1, IO_DE_REGEN_LCK_RAW) && - ADV_REG_GETFIELD(temp1, IO_V_LOCKED_RAW) && - ADV_REG_GETFIELD(temp1, IO_TMDSPLL_LCK_A_RAW) && - ADV_REG_GETFIELD(temp2, CP_STDI_DVALID_CH1)) + if (adv7481_is_timing_locked(state)) break; count++; usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); } while (count < LOCK_NUM_TRIES); if (count >= LOCK_NUM_TRIES) { - pr_err("%s(%d), adv7481 HDMI DE regeneration block NOT Locked: 0x%x", - __func__, __LINE__, temp1); + pr_err("%s(%d), HDMI DE regeneration block NOT Locked\n", + __func__, __LINE__); } /* Check Timing Lock HDMI Map V:0x07[7], H:0x7[5] */ do { - temp1 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, + state->i2c_hdmi_addr, HDMI_REG_LINE_WIDTH_1_ADDR); if (ADV_REG_GETFIELD(temp1, HDMI_VERT_FILTER_LOCKED) && @@ -1059,7 +1613,8 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, } /* Check HDMI Parameters */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_FIELD1_HEIGHT1_ADDR); + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_FIELD1_HEIGHT1_ADDR); hdmi_params->color_depth = ADV_REG_GETFIELD(temp1, HDMI_REG_DEEP_COLOR_MODE); @@ -1068,22 +1623,25 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, HDMI_REG_HDMI_INTERLACED); fieldfactor = (vid_params->intrlcd == 1) ? 2 : 1; - temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_HDMI_PARAM5_ADDR); + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_HDMI_PARAM5_ADDR); hdmi_params->pix_rep = ADV_REG_GETFIELD(temp1, HDMI_REG_PIXEL_REPETITION); /* Get Active Timing Data HDMI Map H:0x07[4:0] + 0x08[7:0] */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_LINE_WIDTH_1_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, HDMI_REG_LINE_WIDTH_2_ADDR); + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_LINE_WIDTH_1_ADDR); + temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, + HDMI_REG_LINE_WIDTH_2_ADDR); vid_params->act_pix = (((ADV_REG_GETFIELD(temp1, HDMI_REG_LINE_WIDTH_1) << 8) & 0x1F00) | ADV_REG_GETFIELD(temp2, HDMI_REG_LINE_WIDTH_2)); /* Get Total Timing Data HDMI Map H:0x1E[5:0] + 0x1F[7:0] */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_TOTAL_LINE_WIDTH_1_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, + temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_TOTAL_LINE_WIDTH_2_ADDR); vid_params->tot_pix = (((ADV_REG_GETFIELD(temp1, HDMI_REG_TOTAL_LINE_WIDTH_1) << 8) & 0x3F00) | @@ -1091,9 +1649,9 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, HDMI_REG_TOTAL_LINE_WIDTH_2)); /* Get Active Timing Data HDMI Map V:0x09[4:0] + 0x0A[7:0] */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_FIELD0_HEIGHT_1_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, + temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_FIELD0_HEIGHT_2_ADDR); vid_params->act_lines = (((ADV_REG_GETFIELD(temp1, HDMI_REG_FIELD0_HEIGHT_1) << 8) & 0x1F00) | @@ -1101,9 +1659,9 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, HDMI_REG_FIELD0_HEIGHT_2)); /* Get Total Timing Data HDMI Map V:0x26[5:0] + 0x27[7:0] */ - temp1 = adv7481_rd_byte(state->i2c_hdmi, + temp1 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_FIELD0_TOTAL_HEIGHT_1_ADDR); - temp2 = adv7481_rd_byte(state->i2c_hdmi, + temp2 = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_FIELD0_TOTAL_HEIGHT_2_ADDR); vid_params->tot_lines = (((ADV_REG_GETFIELD(temp1, HDMI_REG_FIELD0_TOT_HEIGHT_1) << 8) & 0x3F00) | @@ -1139,6 +1697,18 @@ static int adv7481_get_hdmi_timings(struct adv7481_state *state, (hdmi_params->pix_rep + 1)); } +set_default: + if (ret) { + pr_debug("%s(%d), error %d resort to default fmt\n", + __func__, __LINE__, ret); + vid_params->act_pix = MAX_DEFAULT_WIDTH; + vid_params->act_lines = MAX_DEFAULT_HEIGHT; + vid_params->fr_rate = MAX_DEFAULT_FRAME_RATE; + vid_params->pix_clk = MAX_DEFAULT_PIX_CLK_HZ; + vid_params->intrlcd = 0; + ret = 0; + } + pr_debug("%s(%d), adv7481 TMDS Resolution: %d x %d @ %d fps\n", __func__, __LINE__, vid_params->act_pix, vid_params->act_lines, @@ -1170,15 +1740,22 @@ static int adv7481_query_dv_timings(struct v4l2_subdev *sd, switch (state->mode) { case ADV7481_IP_HDMI: case ADV7481_IP_CVBS_1_HDMI_SIM: - adv7481_get_hdmi_timings(state, &vid_params, &hdmi_params); - timings->type = V4L2_DV_BT_656_1120; - bt_timings->width = vid_params.act_pix; - bt_timings->height = vid_params.act_lines; - bt_timings->pixelclock = vid_params.pix_clk; - bt_timings->interlaced = vid_params.intrlcd ? + ret = adv7481_get_hdmi_timings(state, &vid_params, + &hdmi_params); + if (!ret) { + timings->type = V4L2_DV_BT_656_1120; + bt_timings->width = vid_params.act_pix; + bt_timings->height = vid_params.act_lines; + bt_timings->pixelclock = vid_params.pix_clk; + bt_timings->interlaced = vid_params.intrlcd ? V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; - if (bt_timings->interlaced == V4L2_DV_INTERLACED) - bt_timings->height /= 2; + if (bt_timings->interlaced == V4L2_DV_INTERLACED) + bt_timings->height /= 2; + } else { + pr_err( + "%s: Error in adv7481_get_hdmi_timings. ret %d\n", + __func__, ret); + } break; default: return -EINVAL; @@ -1193,10 +1770,24 @@ static int adv7481_query_sd_std(struct v4l2_subdev *sd, v4l2_std_id *std) int temp = 0; struct adv7481_state *state = to_state(sd); uint8_t tStatus = 0x0; + uint32_t count = 0; pr_debug("Enter %s\n", __func__); - tStatus = adv7481_rd_byte(state->i2c_sdp, SDP_RO_MAIN_STATUS1_ADDR); - if (!ADV_REG_GETFIELD(tStatus, SDP_RO_MAIN_IN_LOCK)) + /* Select SDP read-only main Map */ + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x01); + do { + tStatus = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR); + if (ADV_REG_GETFIELD(tStatus, SDP_RO_MAIN_IN_LOCK)) + break; + count++; + usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); + } while (count < LOCK_NUM_TRIES); + + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); + if (count >= LOCK_NUM_TRIES) pr_err("%s(%d), adv7481 SD Input NOT Locked: 0x%x\n", __func__, __LINE__, tStatus); @@ -1245,13 +1836,15 @@ static int adv7481_g_frame_interval(struct v4l2_subdev *sd, return 0; } -static int adv7481_g_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) +static int adv7481_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { int ret; struct adv7481_vid_params vid_params; struct adv7481_hdmi_params hdmi_params; struct adv7481_state *state = to_state(sd); + struct v4l2_mbus_framefmt *fmt = &format->format; if (!fmt) return -EINVAL; @@ -1267,17 +1860,29 @@ static int adv7481_g_mbus_fmt(struct v4l2_subdev *sd, switch (state->mode) { case ADV7481_IP_HDMI: case ADV7481_IP_CVBS_1_HDMI_SIM: - adv7481_get_hdmi_timings(state, &vid_params, &hdmi_params); - fmt->width = vid_params.act_pix; - fmt->height = vid_params.act_lines; - if (vid_params.intrlcd) - fmt->height /= 2; + ret = adv7481_get_hdmi_timings(state, &vid_params, + &hdmi_params); + if (!ret) { + fmt->width = vid_params.act_pix; + fmt->height = vid_params.act_lines; + if (vid_params.intrlcd) + fmt->height /= 2; + } else { + pr_err("%s: Error %d in adv7481_get_hdmi_timings\n", + __func__, ret); + } + break; + case ADV7481_IP_CVBS_1: + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->width = 720; + fmt->height = 576; break; default: return -EINVAL; } mutex_unlock(&state->mutex); - fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; return ret; } @@ -1290,17 +1895,17 @@ static int adv7481_set_audio_spdif(struct adv7481_state *state, if (on) { /* Configure I2S_SDATA output pin as an SPDIF output 0x6E[3] */ - val = adv7481_rd_byte(state->i2c_hdmi, + val = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_MUX_SPDIF_TO_I2S_ADDR); val |= ADV_REG_SETFIELD(1, HDMI_MUX_SPDIF_TO_I2S_EN); - ret = adv7481_wr_byte(state->i2c_hdmi, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_MUX_SPDIF_TO_I2S_ADDR, val); } else { /* Configure I2S_SDATA output pin as an I2S output 0x6E[3] */ - val = adv7481_rd_byte(state->i2c_hdmi, + val = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_MUX_SPDIF_TO_I2S_ADDR); val &= ~ADV_REG_SETFIELD(1, HDMI_MUX_SPDIF_TO_I2S_EN); - ret = adv7481_wr_byte(state->i2c_hdmi, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_hdmi_addr, HDMI_REG_MUX_SPDIF_TO_I2S_ADDR, val); } return ret; @@ -1310,40 +1915,42 @@ static int adv7481_csi_powerdown(struct adv7481_state *state, enum adv7481_output output) { int ret; - struct i2c_client *csi_map; + uint8_t csi_map; uint8_t val = 0; pr_debug("Enter %s for output: %d\n", __func__, output); /* Select CSI TX to configure data */ if (output == ADV7481_OP_CSIA) { - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } else if (output == ADV7481_OP_CSIB) { - csi_map = state->i2c_csi_txb; + csi_map = state->i2c_csi_txb_addr; } else if (output == ADV7481_OP_TTL) { /* For now use TxA */ - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } else { /* Default to TxA */ - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } /* CSI Tx: power down DPHY */ - ret = adv7481_wr_byte(csi_map, CSI_REG_TX_DPHY_PWDN_ADDR, + ret = adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_DPHY_PWDN_ADDR, ADV_REG_SETFIELD(1, CSI_CTRL_DPHY_PWDN)); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0x31, 0x82); - ret |= adv7481_wr_byte(csi_map, 0x1e, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x31, 0x82); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x1e, 0x00); /* CSI TxA: # Lane : Power Off */ val = ADV_REG_SETFIELD(1, CSI_CTRL_TX_PWRDN) | ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES); - ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_CFG1_ADDR, val); /* * ADI Recommended power down sequence * DPHY and CSI Tx A Power down Sequence * CSI TxA: MIPI PLL DIS */ - ret |= adv7481_wr_byte(csi_map, 0xda, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xda, 0x00); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0xc1, 0x3b); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xc1, 0x3b); pr_debug("Exit %s, ret: %d\n", __func__, ret); @@ -1354,17 +1961,23 @@ static int adv7481_csi_powerup(struct adv7481_state *state, enum adv7481_output output) { int ret; - struct i2c_client *csi_map; + uint8_t csi_map; uint8_t val = 0; uint8_t csi_sel = 0; pr_debug("Enter %s for output: %d\n", __func__, output); /* Select CSI TX to configure data */ if (output == ADV7481_OP_CSIA) { - csi_sel = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | - ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | - ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); - csi_map = state->i2c_csi_txa; + if (state->csia_src == ADV7481_IP_HDMI) { + csi_sel = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | + ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | + ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); + } else { + csi_sel = ADV_REG_SETFIELD(1, IO_CTRL_CSI4_EN) | + ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | + ADV_REG_SETFIELD(0x2, IO_CTRL_CSI4_IN_SEL); + } + csi_map = state->i2c_csi_txa_addr; } else if (output == ADV7481_OP_CSIB) { /* Enable 1-Lane MIPI Tx, enable pixel output and * route SD through Pixel port @@ -1373,54 +1986,57 @@ static int adv7481_csi_powerup(struct adv7481_state *state, ADV_REG_SETFIELD(1, IO_CTRL_PIX_OUT_EN) | ADV_REG_SETFIELD(1, IO_CTRL_SD_THRU_PIX_OUT) | ADV_REG_SETFIELD(0, IO_CTRL_CSI4_IN_SEL); - csi_map = state->i2c_csi_txb; + csi_map = state->i2c_csi_txb_addr; } else if (output == ADV7481_OP_TTL) { /* For now use TxA */ - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } else { /* Default to TxA */ - csi_map = state->i2c_csi_txa; + csi_map = state->i2c_csi_txa_addr; } /* Enable Tx A/B CSI #-lane */ - ret = adv7481_wr_byte(state->client, + ret = adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, IO_REG_CSI_PIX_EN_SEL_ADDR, csi_sel); /* TXA MIPI lane settings for CSI */ /* CSI TxA: # Lane : Power Off */ val = ADV_REG_SETFIELD(1, CSI_CTRL_TX_PWRDN) | ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES); - ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_CFG1_ADDR, val); /* CSI TxA: Auto D-PHY Timing */ val |= ADV_REG_SETFIELD(1, CSI_CTRL_AUTO_PARAMS); - ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_CFG1_ADDR, val); /* DPHY and CSI Tx A */ - ret |= adv7481_wr_byte(csi_map, 0xdb, 0x10); - ret |= adv7481_wr_byte(csi_map, 0xd6, 0x07); - ret |= adv7481_wr_byte(csi_map, 0xc4, 0x0a); - ret |= adv7481_wr_byte(csi_map, 0x71, 0x33); - ret |= adv7481_wr_byte(csi_map, 0x72, 0x11); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xdb, 0x10); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xd6, 0x07); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xc4, 0x0a); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x71, 0x33); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x72, 0x11); /* CSI TxA: power up DPHY */ - ret |= adv7481_wr_byte(csi_map, 0xf0, 0x00); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xf0, 0x00); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0x31, 0x82); - ret |= adv7481_wr_byte(csi_map, 0x1e, 0x40); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x31, 0x82); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x1e, 0x40); /* adi Recommended power up sequence */ /* DPHY and CSI Tx A Power up Sequence */ /* CSI TxA: MIPI PLL EN */ - ret |= adv7481_wr_byte(csi_map, 0xda, 0x01); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xda, 0x01); msleep(200); /* CSI TxA: # MIPI Lane : Power ON */ val = ADV_REG_SETFIELD(0, CSI_CTRL_TX_PWRDN) | ADV_REG_SETFIELD(1, CSI_CTRL_AUTO_PARAMS) | ADV_REG_SETFIELD(state->tx_lanes, CSI_CTRL_NUM_LANES); - ret |= adv7481_wr_byte(csi_map, CSI_REG_TX_CFG1_ADDR, val); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, + CSI_REG_TX_CFG1_ADDR, val); msleep(100); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0xc1, 0x2b); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0xc1, 0x2b); msleep(100); /* ADI Required Write */ - ret |= adv7481_wr_byte(csi_map, 0x31, 0x80); + ret |= adv7481_wr_byte(&state->i2c_client, csi_map, 0x31, 0x80); pr_debug("Exit %s, ret: %d\n", __func__, ret); @@ -1476,42 +2092,43 @@ static int adv7481_g_input_status(struct v4l2_subdev *sd, u32 *status) { int ret = 0; struct adv7481_state *state = to_state(sd); - uint8_t val1 = 0; - uint8_t val2 = 0; + uint8_t val = 0; uint32_t count = 0; + *status = 0; pr_debug("Enter %s\n", __func__); if (ADV7481_IP_HDMI == state->mode) { - /* - * Check Timing Lock IO Map Status3:0x71[0] && - * 0x71[1] && 0x71[7] - */ + /* Check Timing Lock */ do { - val1 = adv7481_rd_byte(state->client, - IO_HDMI_LVL_RAW_STATUS_3_ADDR); - val2 = adv7481_rd_byte(state->i2c_cp, - CP_REG_STDI_CH_ADDR); - - if (ADV_REG_GETFIELD(val1, IO_DE_REGEN_LCK_RAW) && - ADV_REG_GETFIELD(val1, IO_V_LOCKED_RAW) && - ADV_REG_GETFIELD(val1, IO_TMDSPLL_LCK_A_RAW) && - ADV_REG_GETFIELD(val2, CP_STDI_DVALID_CH1)) + if (adv7481_is_timing_locked(state)) break; count++; usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); } while (count < LOCK_NUM_TRIES); if (count >= LOCK_NUM_TRIES) { - pr_err("%s(%d), HDMI DE regeneration block NOT Locked: 0x%x, 0x%x", - __func__, __LINE__, val1, val2); + pr_err("%s(%d), HDMI DE regeneration block NOT Locked\n", + __func__, __LINE__); *status |= V4L2_IN_ST_NO_SIGNAL; } } else { - val1 = adv7481_rd_byte(state->i2c_sdp, - SDP_RO_MAIN_STATUS1_ADDR); - if (!ADV_REG_GETFIELD(val1, SDP_RO_MAIN_IN_LOCK)) { + /* Select SDP read-only main Map */ + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x01); + do { + val = adv7481_rd_byte(&state->i2c_client, + state->i2c_sdp_addr, SDP_RO_MAIN_STATUS1_ADDR); + if (ADV_REG_GETFIELD(val, SDP_RO_MAIN_IN_LOCK)) + break; + count++; + usleep_range(LOCK_MIN_SLEEP, LOCK_MAX_SLEEP); + } while (count < LOCK_NUM_TRIES); + + adv7481_wr_byte(&state->i2c_client, state->i2c_sdp_addr, + SDP_RW_MAP_REG, 0x00); + if (count >= LOCK_NUM_TRIES) { pr_err("%s(%d), SD Input NOT Locked: 0x%x\n", - __func__, __LINE__, val1); + __func__, __LINE__, val); *status |= V4L2_IN_ST_NO_SIGNAL; } } @@ -1530,7 +2147,6 @@ static int adv7481_s_stream(struct v4l2_subdev *sd, int on) static const struct v4l2_subdev_video_ops adv7481_video_ops = { .s_routing = adv7481_s_routing, .g_frame_interval = adv7481_g_frame_interval, - .g_mbus_fmt = adv7481_g_mbus_fmt, .querystd = adv7481_query_sd_std, .g_dv_timings = adv7481_query_dv_timings, .g_input_status = adv7481_g_input_status, @@ -1539,6 +2155,11 @@ static const struct v4l2_subdev_video_ops adv7481_video_ops = { static const struct v4l2_subdev_core_ops adv7481_core_ops = { .s_power = adv7481_s_power, + .ioctl = adv7481_ioctl, +}; + +static const struct v4l2_subdev_pad_ops adv7481_pad_ops = { + .get_fmt = adv7481_get_fmt, }; static const struct v4l2_ctrl_ops adv7481_ctrl_ops = { @@ -1548,10 +2169,13 @@ static const struct v4l2_ctrl_ops adv7481_ctrl_ops = { static const struct v4l2_subdev_ops adv7481_ops = { .core = &adv7481_core_ops, .video = &adv7481_video_ops, + .pad = &adv7481_pad_ops, }; static int adv7481_init_v4l2_controls(struct adv7481_state *state) { + int ret = 0; + v4l2_ctrl_handler_init(&state->ctrl_hdl, 4); v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7481_ctrl_ops, @@ -1565,59 +2189,195 @@ static int adv7481_init_v4l2_controls(struct adv7481_state *state) state->sd.ctrl_handler = &state->ctrl_hdl; if (state->ctrl_hdl.error) { - int err = state->ctrl_hdl.error; + ret = state->ctrl_hdl.error; v4l2_ctrl_handler_free(&state->ctrl_hdl); - return err; + } else { + v4l2_ctrl_handler_setup(&state->ctrl_hdl); } - v4l2_ctrl_handler_setup(&state->ctrl_hdl); - return 0; + pr_err("%s: Exit with ret: %d\n", __func__, ret); + return ret; +} + +static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = { + .i2c_read = msm_camera_cci_i2c_read, + .i2c_read_seq = msm_camera_cci_i2c_read_seq, + .i2c_write = msm_camera_cci_i2c_write, + .i2c_write_seq = msm_camera_cci_i2c_write_seq, + .i2c_write_table = msm_camera_cci_i2c_write_table, + .i2c_write_seq_table = msm_camera_cci_i2c_write_seq_table, + .i2c_write_table_w_microdelay = + msm_camera_cci_i2c_write_table_w_microdelay, + .i2c_util = msm_sensor_cci_i2c_util, + .i2c_poll = msm_camera_cci_i2c_poll, +}; + +static int adv7481_cci_init(struct adv7481_state *state) +{ + struct msm_camera_cci_client *cci_client = NULL; + int ret = 0; + + pr_err("%s: Enter\n", __func__); + + state->i2c_client.i2c_func_tbl = &msm_sensor_cci_func_tbl; + state->i2c_client.addr_type = MSM_CAMERA_I2C_BYTE_ADDR; + state->i2c_client.cci_client = kzalloc(sizeof( + struct msm_camera_cci_client), GFP_KERNEL); + cci_client = state->i2c_client.cci_client; + if (!cci_client) { + ret = -ENOMEM; + goto err_cci_init; + } + cci_client->cci_subdev = msm_cci_get_subdev(); + pr_debug("%s cci_subdev: %p\n", __func__, cci_client->cci_subdev); + if (!cci_client->cci_subdev) { + ret = -EPROBE_DEFER; + goto err_cci_init; + } + cci_client->cci_i2c_master = state->cci_master; + cci_client->sid = state->i2c_slave_addr; + cci_client->retries = 3; + cci_client->id_map = 0; + cci_client->i2c_freq_mode = I2C_CUSTOM_MODE; + ret = state->i2c_client.i2c_func_tbl->i2c_util( + &state->i2c_client, MSM_CCI_INIT); + if (ret < 0) + pr_err("%s - cci_init failed\n", __func__); + else + state->clocks_requested = TRUE; + + pr_debug("%s i2c_client.client: %p\n", __func__, + state->i2c_client.client); + +err_cci_init: + return ret; } -static int adv7481_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adv7481_parse_dt(struct platform_device *pdev, + struct adv7481_state *state) +{ + struct device_node *np = state->dev->of_node; + uint32_t i = 0; + int gpio_count = 0; + struct resource *adv_addr_res = NULL; + int ret = 0; + + /* config CCI */ + ret = of_property_read_u32(np, "qcom,cci-master", + &state->cci_master); + if (ret < 0 || state->cci_master >= MASTER_MAX) { + pr_err("%s: failed to read cci master . ret %d\n", + __func__, ret); + goto exit; + } + pr_debug("%s: cci_master: 0x%x\n", __func__, state->cci_master); + adv_addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!adv_addr_res) { + pr_err("%s: failed to read adv7481 resource.\n", __func__); + goto exit; + } + state->i2c_slave_addr = adv_addr_res->start; + pr_debug("%s: i2c_slave_addr: 0x%x\n", __func__, state->i2c_slave_addr); + state->i2c_io_addr = (uint8_t)state->i2c_slave_addr; + + gpio_count = of_gpio_count(np); + if (gpio_count != ADV7481_GPIO_MAX) { + ret = -EFAULT; + pr_err("%s: dt gpio count %d doesn't match required. ret %d\n", + __func__, gpio_count, ret); + goto exit; + } + for (i = 0; i < ADV7481_GPIO_MAX; i++) { + state->gpio_array[i].gpio = of_get_gpio_flags(np, i, + (enum of_gpio_flags *)&state->gpio_array[i].flags); + if (!gpio_is_valid(state->gpio_array[i].gpio)) { + pr_err("invalid gpio setting for index %d\n", i); + ret = -EFAULT; + goto exit; + } + pr_debug("%s: gpio_array[%d] = %d flag = %ld\n", __func__, i, + state->gpio_array[i].gpio, state->gpio_array[i].flags); + } + +exit: + return ret; +} + +static const struct of_device_id adv7481_id[] = { + { .compatible = "qcom,adv7481", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, adv7481_id); + +static int adv7481_probe(struct platform_device *pdev) { struct adv7481_state *state; - struct adv7481_platform_data *pdata = NULL; + const struct of_device_id *device_id; struct v4l2_subdev *sd; - struct v4l2_ctrl_handler *hdl; int ret; - pr_debug("Attempting to probe...\n"); - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA)) { - pr_err("%s %s Check i2c Functionality Fail\n", - __func__, client->name); - ret = -EIO; + device_id = of_match_device(adv7481_id, &pdev->dev); + if (!device_id) { + pr_err("%s: device_id is NULL\n", __func__); + ret = -ENODEV; goto err; } - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr, client->adapter->name); /* Create 7481 State */ - state = devm_kzalloc(&client->dev, - sizeof(struct adv7481_state), GFP_KERNEL); + state = devm_kzalloc(&pdev->dev, + sizeof(struct adv7481_state), GFP_KERNEL); if (state == NULL) { ret = -ENOMEM; - pr_err("Check Kzalloc Fail\n"); - goto err_mem; + goto err; } - state->client = client; + platform_set_drvdata(pdev, state); + state->dev = &pdev->dev; + mutex_init(&state->mutex); + ret = adv7481_parse_dt(pdev, state); + if (ret < 0) { + pr_err("Error parsing dt tree\n"); + goto err_mem_free; + } - /* Get and Check Platform Data */ - pdata = (struct adv7481_platform_data *) client->dev.platform_data; - if (!pdata) { - ret = -ENOMEM; - pr_err("Getting Platform data failed\n"); - goto err_mem; + ret = adv7481_cci_init(state); + if (ret < 0) { + pr_err("%s: failed adv7481_cci_init ret %d\n", __func__, ret); + goto err_mem_free; + } + + /* config VREG */ + ret = msm_camera_get_dt_vreg_data(pdev->dev.of_node, + &(state->cci_vreg), &(state->regulator_count)); + if (ret < 0) { + pr_err("%s:cci get_dt_vreg failed\n", __func__); + goto err_mem_free; + } + + ret = msm_camera_config_vreg(&pdev->dev, state->cci_vreg, + state->regulator_count, NULL, 0, + &state->cci_reg_ptr[0], 1); + if (ret < 0) { + pr_err("%s:cci config_vreg failed\n", __func__); + goto err_mem_free; + } + + ret = msm_camera_enable_vreg(&pdev->dev, state->cci_vreg, + state->regulator_count, NULL, 0, + &state->cci_reg_ptr[0], 1); + if (ret < 0) { + pr_err("%s:cci enable_vreg failed\n", __func__); + goto err_mem_free; } + pr_debug("%s - VREG Initialized...\n", __func__); - /* Configure and Register V4L2 I2C Sub-device */ + /* Configure and Register V4L2 Sub-device */ sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &adv7481_ops); + v4l2_subdev_init(sd, &adv7481_ops); + sd->owner = pdev->dev.driver->owner; + v4l2_set_subdevdata(sd, state); + strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); state->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; state->sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS; @@ -1627,103 +2387,136 @@ static int adv7481_probe(struct i2c_client *client, ret = media_entity_init(&state->sd.entity, 1, &state->pad, 0); if (ret) { ret = -EIO; - pr_err("Media entity init failed\n"); + pr_err("%s(%d): Media entity init failed\n", + __func__, __LINE__); goto err_media_entity; } /* Initialize HW Config */ - ret = adv7481_hw_init(pdata, state); + ret = adv7481_hw_init(state); if (ret) { ret = -EIO; - pr_err("HW Initialisation Failed\n"); + pr_err("%s: HW Initialisation Failed\n", __func__); goto err_media_entity; } /* Register V4l2 Control Functions */ - hdl = &state->ctrl_hdl; - v4l2_ctrl_handler_init(hdl, 4); - adv7481_init_v4l2_controls(state); + ret = adv7481_init_v4l2_controls(state); + if (ret) { + pr_err("%s: V4L2 Controls Initialisation Failed %d\n", + __func__, ret); + } - /* Initials ADV7481 State Settings */ + /* Initial ADV7481 State Settings */ state->tx_auto_params = ADV7481_AUTO_PARAMS; - state->tx_lanes = ADV7481_MIPI_2LANE; /* Initialize SW Init Settings and I2C sub maps 7481 */ - ret = adv7481_dev_init(state, client); + ret = adv7481_dev_init(state); if (ret) { ret = -EIO; - pr_err("SW Initialisation Failed\n"); + pr_err("%s(%d): SW Initialisation Failed\n", + __func__, __LINE__); goto err_media_entity; } - /* Set hdmi settings */ - ret = adv7481_set_hdmi_mode(state); - /* BA registration */ - ret |= msm_ba_register_subdev_node(sd); + ret = msm_ba_register_subdev_node(sd); if (ret) { ret = -EIO; - pr_err("BA INIT FAILED\n"); + pr_err("%s: BA init failed\n", __func__); goto err_media_entity; } + enable_irq(state->irq); pr_debug("Probe successful!\n"); return ret; err_media_entity: media_entity_cleanup(&sd->entity); -err_mem: - kfree(state); + +err_mem_free: + adv7481_release_cci_clks(state); + devm_kfree(&pdev->dev, state); + err: - if (!ret) - ret = 1; return ret; } -static int adv7481_remove(struct i2c_client *client) +static int adv7481_remove(struct platform_device *pdev) { - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct adv7481_state *state = to_state(sd); + struct adv7481_state *state = platform_get_drvdata(pdev); - msm_ba_unregister_subdev_node(sd); - v4l2_device_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); + msm_ba_unregister_subdev_node(&state->sd); + v4l2_device_unregister_subdev(&state->sd); + media_entity_cleanup(&state->sd.entity); v4l2_ctrl_handler_free(&state->ctrl_hdl); + adv7481_reset_irq(state); if (state->irq > 0) free_irq(state->irq, state); - i2c_unregister_device(state->i2c_csi_txa); - i2c_unregister_device(state->i2c_csi_txb); - i2c_unregister_device(state->i2c_hdmi); - i2c_unregister_device(state->i2c_edid); - i2c_unregister_device(state->i2c_cp); - i2c_unregister_device(state->i2c_sdp); - i2c_unregister_device(state->i2c_rep); + cancel_delayed_work(&state->irq_delayed_work); mutex_destroy(&state->mutex); - kfree(state); + devm_kfree(&pdev->dev, state); return 0; } -static const struct i2c_device_id adv7481_id[] = { - { DRIVER_NAME, 0 }, - {}, -}; -MODULE_DEVICE_TABLE(i2c, adv7481_id); +#ifdef CONFIG_PM_SLEEP +static int adv7481_suspend(struct device *dev) +{ + struct adv7481_state *state; + int ret; + + state = (struct adv7481_state *)dev_get_drvdata(dev); + + /* release CCI clocks */ + ret = adv7481_release_cci_clks(state); + if (ret) + pr_err("%s: adv7481 release cci clocks failed\n", __func__); + else + pr_debug("released cci clocks in suspend"); + + return 0; +} + +static int adv7481_resume(struct device *dev) +{ + struct adv7481_state *state; + int ret; + + state = (struct adv7481_state *)dev_get_drvdata(dev); + + /* Request CCI clocks */ + ret = adv7481_request_cci_clks(state); + if (ret) + pr_err("%s: adv7481 request cci clocks failed\n", __func__); + else + pr_debug("requested cci clocks in resume"); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(adv7481_pm_ops, adv7481_suspend, adv7481_resume); +#define ADV7481_PM_OPS (&adv7481_pm_ops) +#else +#define ADV7481_PM_OPS NULL +#endif -static struct i2c_driver adv7481_driver = { +static struct platform_driver adv7481_driver = { .driver = { .owner = THIS_MODULE, .name = KBUILD_MODNAME, + .of_match_table = adv7481_id, + .pm = ADV7481_PM_OPS, }, .probe = adv7481_probe, .remove = adv7481_remove, - .id_table = adv7481_id, }; -module_i2c_driver(adv7481_driver); +module_driver(adv7481_driver, platform_driver_register, + platform_driver_unregister); MODULE_DESCRIPTION("ADI ADV7481 HDMI/MHL/SD video receiver"); diff --git a/drivers/media/i2c/adv7481_reg.h b/drivers/media/i2c/adv7481_reg.h index a4e14fa18e0a..60b1301abbe6 100644 --- a/drivers/media/i2c/adv7481_reg.h +++ b/drivers/media/i2c/adv7481_reg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -19,329 +19,451 @@ #define ADV_REG_GETFIELD(val, field) \ (((val) & (field##_BMSK)) >> (field##_SHFT)) +#define ADV_REG_RSTFIELD(val, field) \ + ((val) & ~((field##_BMSK) << (field##_SHFT))) + /* IO Map Registers */ -#define IO_REG_MAIN_RST_ADDR 0xFF -#define IO_REG_MAIN_RST_VALUE 0xFF - -#define IO_REG_PWR_DOWN_CTRL_ADDR 0x00 -#define IO_CTRL_RX_EN_BMSK 0x0040 -#define IO_CTRL_RX_EN_SHFT 6 -#define IO_CTRL_RX_PWDN_BMSK 0x0020 -#define IO_CTRL_RX_PWDN_SHFT 5 -#define IO_CTRL_XTAL_PWDN_BMSK 0x0004 -#define IO_CTRL_XTAL_PWDN_SHFT 2 -#define IO_CTRL_CORE_PWDN_BMSK 0x0002 -#define IO_CTRL_CORE_PWDN_SHFT 1 -#define IO_CTRL_MASTER_PWDN_BMSK 0x0001 -#define IO_CTRL_MASTER_PWDN_SHFT 0 - -#define IO_REG_PWR_DN2_XTAL_HIGH_ADDR 0x01 -#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_BMSK 0x0080 -#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_SHFT 7 -#define IO_CTRL_CEC_WAKE_UP_PWRDNB_BMSK 0x0040 -#define IO_CTRL_CEC_WAKE_UP_PWRDNB_SHFT 6 -#define IO_PROG_XTAL_FREQ_HIGH_BMSK 0x003F -#define IO_PROG_XTAL_FREQ_HIGH_SHFT 0 - -#define IO_REG_XTAL_FREQ_LOW_ADDR 0x02 -#define IO_PROG_XTAL_FREQ_LOW_BMSK 0x00FF -#define IO_PROG_XTAL_FREQ_LOW_SHFT 0 - -#define IO_REG_CP_VID_STD_ADDR 0x05 - -#define IO_REG_CSI_PIX_EN_SEL_ADDR 0x10 -#define IO_CTRL_CSI4_EN_BMSK 0x0080 -#define IO_CTRL_CSI4_EN_SHFT 7 -#define IO_CTRL_CSI1_EN_BMSK 0x0040 -#define IO_CTRL_CSI1_EN_SHFT 6 -#define IO_CTRL_PIX_OUT_EN_BMSK 0x0020 -#define IO_CTRL_PIX_OUT_EN_SHFT 5 -#define IO_CTRL_SD_THRU_PIX_OUT_BMSK 0x0010 -#define IO_CTRL_SD_THRU_PIX_OUT_SHFT 4 -#define IO_CTRL_CSI4_IN_SEL_BMSK 0x000C -#define IO_CTRL_CSI4_IN_SEL_SHFT 2 - -#define IO_PAD_CTRLS_ADDR 0x0E - -#define IO_REG_I2C_CFG_ADDR 0xF2 -#define IO_REG_I2C_AUTOINC_EN_REG_VALUE 0x01 - -#define IO_CTRL_MASTER_PWDN_REG_VALUE 0x01 - -#define IO_HDMI_LVL_RAW_STATUS_3_ADDR 0x71 -#define IO_TMDSPLL_LCK_A_RAW_BMSK 0x0080 -#define IO_TMDSPLL_LCK_A_RAW_SHFT 7 -#define IO_CABLE_DET_A_RAW_BMSK 0x0040 -#define IO_CABLE_DET_A_RAW_SHFT 6 -#define IO_V_LOCKED_RAW_BMSK 0x0002 -#define IO_V_LOCKED_RAW_SHFT 1 -#define IO_DE_REGEN_LCK_RAW_BMSK 0x0001 -#define IO_DE_REGEN_LCK_RAW_SHFT 0 +#define IO_REG_MAIN_RST_ADDR 0xFF +#define IO_REG_MAIN_RST_VALUE 0xFF + +#define IO_REG_PWR_DOWN_CTRL_ADDR 0x00 +#define IO_CTRL_RX_EN_BMSK 0x0040 +#define IO_CTRL_RX_EN_SHFT 6 +#define IO_CTRL_RX_PWDN_BMSK 0x0020 +#define IO_CTRL_RX_PWDN_SHFT 5 +#define IO_CTRL_XTAL_PWDN_BMSK 0x0004 +#define IO_CTRL_XTAL_PWDN_SHFT 2 +#define IO_CTRL_CORE_PWDN_BMSK 0x0002 +#define IO_CTRL_CORE_PWDN_SHFT 1 +#define IO_CTRL_MASTER_PWDN_BMSK 0x0001 +#define IO_CTRL_MASTER_PWDN_SHFT 0 + +#define IO_REG_PWR_DN2_XTAL_HIGH_ADDR 0x01 +#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_BMSK 0x0080 +#define IO_CTRL_CEC_WAKE_UP_PWRDN2B_SHFT 7 +#define IO_CTRL_CEC_WAKE_UP_PWRDNB_BMSK 0x0040 +#define IO_CTRL_CEC_WAKE_UP_PWRDNB_SHFT 6 +#define IO_PROG_XTAL_FREQ_HIGH_BMSK 0x003F +#define IO_PROG_XTAL_FREQ_HIGH_SHFT 0 + +#define IO_REG_XTAL_FREQ_LOW_ADDR 0x02 +#define IO_PROG_XTAL_FREQ_LOW_BMSK 0x00FF +#define IO_PROG_XTAL_FREQ_LOW_SHFT 0 + +#define IO_REG_CP_VID_STD_ADDR 0x05 + +#define IO_REG_CSI_PIX_EN_SEL_ADDR 0x10 +#define IO_CTRL_CSI4_EN_BMSK 0x0080 +#define IO_CTRL_CSI4_EN_SHFT 7 +#define IO_CTRL_CSI1_EN_BMSK 0x0040 +#define IO_CTRL_CSI1_EN_SHFT 6 +#define IO_CTRL_PIX_OUT_EN_BMSK 0x0020 +#define IO_CTRL_PIX_OUT_EN_SHFT 5 +#define IO_CTRL_SD_THRU_PIX_OUT_BMSK 0x0010 +#define IO_CTRL_SD_THRU_PIX_OUT_SHFT 4 +#define IO_CTRL_CSI4_IN_SEL_BMSK 0x000C +#define IO_CTRL_CSI4_IN_SEL_SHFT 2 + +#define IO_PAD_CTRLS_ADDR 0x0E +#define IO_PAD_FILTER_CTRLS_ADDR 0x0F + +#define IO_REG_I2C_CFG_ADDR 0xF2 +#define IO_REG_I2C_AUTOINC_EN_REG_VALUE 0x01 + +#define IO_CTRL_MASTER_PWDN_REG_VALUE 0x01 /* Interrupts */ -#define IO_HDMI_LVL_INT_STATUS_3_ADDR 0x72 -#define IO_CABLE_DET_A_ST_BMSK 0x0040 -#define IO_CABLE_DET_A_ST_SHFT 6 - -#define IO_HDMI_LVL_INT_CLEAR_3_ADDR 0x73 -#define IO_CABLE_DET_A_CLR_BMSK 0x0040 -#define IO_CABLE_DET_A_CLR_SHFT 6 - -#define IO_HDMI_LVL_INT2_MASKB_3_ADDR 0x74 -#define IO_CABLE_DET_A_MB2_BMSK 0x0040 -#define IO_CABLE_DET_A_MB2_SHFT 6 - -#define IO_HDMI_LVL_INT_MASKB_3_ADDR 0x75 -#define IO_CABLE_DET_A_MB1_BMSK 0x0040 -#define IO_CABLE_DET_A_MB1_SHFT 6 - -#define IO_REG_PAD_CTRL_1_ADDR 0x1D -#define IO_PDN_INT1_BMSK 0x0080 -#define IO_PDN_INT1_SHFT 7 -#define IO_PDN_INT2_BMSK 0x0040 -#define IO_PDN_INT2_SHFT 6 -#define IO_PDN_INT3_BMSK 0x0020 -#define IO_PDN_INT3_SHFT 5 -#define IO_INV_LLC_BMSK 0x0010 -#define IO_INV_LLC_SHFT 4 -#define IO_DRV_LLC_PAD_BMSK 0x000C -#define IO_DRV_LLC_PAD_SHFT 2 - -#define IO_REG_INT_RAW_STATUS_ADDR 0x3F - -#define IO_REG_INT1_CONF_ADDR 0x40 -#define IO_INTRQ_DUR_SEL_BMSK 0x00C0 -#define IO_INTRQ_DUR_SEL_SHFT 6 -#define IO_INTRQ_OP_SEL_BMSK 0x0003 -#define IO_INTRQ_OP_SEL_SHFT 0 - -#define IO_REG_INT2_CONF_ADDR 0x41 -#define IO_INTRQ2_DUR_SEL_BMSK 0x00C0 -#define IO_INTRQ2_DUR_SEL_SHFT 6 -#define IO_CP_LOCK_UNLOCK_EDGE_SEL_BMSK 0x0020 -#define IO_CP_LOCK_UNLOCK_EDGE_SEL_SHFT 5 -#define IO_EN_UMASK_RAW_INTRQ2_BMSK 0x0008 -#define IO_EN_UMASK_RAW_INTRQ2_SHFT 3 -#define IO_INT2_EN_BMSK 0x0004 -#define IO_INT2_EN_SHFT 2 -#define IO_INTRQ2_OP_SEL_BMSK 0x0003 -#define IO_INTRQ2_OP_SEL_SHFT 0 - -#define IO_REG_DATAPATH_RAW_STATUS_ADDR 0x43 -#define IO_REG_DATAPATH_INT_STATUS_ADDR 0x44 -#define IO_REG_DATAPATH_INT_CLEAR_ADDR 0x45 - -#define IO_REG_DATAPATH_INT_MASKB_ADDR 0x47 -#define IO_CP_LOCK_CP_MB1_BMSK 0x0080 -#define IO_CP_LOCK_CP_MB1_SHFT 7 -#define IO_CP_UNLOCK_CP_MB1_BMSK 0x0040 -#define IO_CP_UNLOCK_CP_MB1_SHFT 6 -#define IO_VMUTE_REQUEST_HDMI_MB1_BMSK 0x0020 -#define IO_VMUTE_REQUEST_HDMI_MB1_SHFT 5 -#define IO_MPU_STIM_INTRQ_MB1_BMSK 0x0002 -#define IO_MPU_STIM_INTRQ_MB1_SHFT 1 -#define IO_INT_SD_MB1_BMSK 0x0001 -#define IO_INT_SD_MB1_SHFT 0 +#define IO_HDMI_LVL_INT_CLEAR_1_ADDR 0x69 + +#define IO_HDMI_LVL_INT_MASKB_1_ADDR 0x6B +#define IO_AVI_INFO_MB1_BMSK 0x0001 +#define IO_AVI_INFO_MB1_SHFT 0 + +#define IO_HDMI_LVL_INT_CLEAR_2_ADDR 0x6E + +#define IO_HDMI_LVL_RAW_STATUS_3_ADDR 0x71 +#define IO_TMDSPLL_LCK_A_RAW_BMSK 0x0080 +#define IO_TMDSPLL_LCK_A_RAW_SHFT 7 +#define IO_CABLE_DET_A_RAW_BMSK 0x0040 +#define IO_CABLE_DET_A_RAW_SHFT 6 +#define IO_V_LOCKED_RAW_BMSK 0x0002 +#define IO_V_LOCKED_RAW_SHFT 1 +#define IO_DE_REGEN_LCK_RAW_BMSK 0x0001 +#define IO_DE_REGEN_LCK_RAW_SHFT 0 + +#define IO_HDMI_LVL_INT_STATUS_3_ADDR 0x72 +#define IO_CABLE_DET_A_ST_BMSK 0x0040 +#define IO_CABLE_DET_A_ST_SHFT 6 +#define IO_V_LOCKED_ST_BMSK 0x0002 +#define IO_V_LOCKED_ST_SHFT 1 +#define IO_DE_REGEN_LCK_ST_BMSK 0x0001 +#define IO_DE_REGEN_LCK_ST_SHFT 0 + +#define IO_HDMI_LVL_INT_CLEAR_3_ADDR 0x73 +#define IO_CABLE_DET_A_CLR_BMSK 0x0040 +#define IO_CABLE_DET_A_CLR_SHFT 6 + +#define IO_HDMI_LVL_INT2_MASKB_3_ADDR 0x74 +#define IO_CABLE_DET_A_MB2_BMSK 0x0040 +#define IO_CABLE_DET_A_MB2_SHFT 6 + +#define IO_HDMI_LVL_INT_MASKB_3_ADDR 0x75 +#define IO_CABLE_DET_A_MB1_BMSK 0x0040 +#define IO_CABLE_DET_A_MB1_SHFT 6 +#define IO_V_LOCKED_MB1_BMSK 0x0002 +#define IO_V_LOCKED_MB1_SHFT 1 +#define IO_DE_REGEN_LCK_MB1_BMSK 0x0001 +#define IO_DE_REGEN_LCK_MB1_SHFT 0 + +#define IO_HDMI_EDG_RAW_STATUS_1_ADDR 0x80 +#define IO_NEW_AVI_INFO_RAW_BMSK 0x0001 +#define IO_NEW_AVI_INFO_RAW_SHFT 0 + +#define IO_HDMI_EDG_INT_STATUS_1_ADDR 0x81 +#define IO_NEW_AVI_INFO_ST_BMSK 0x0001 +#define IO_NEW_AVI_INFO_ST_SHFT 0 + +#define IO_HDMI_EDG_INT_CLEAR_1_ADDR 0x82 +#define IO_NEW_AVI_INFO_CLR_BMSK 0x0001 +#define IO_NEW_AVI_INFO_CLR_SHFT 0 + +#define IO_HDMI_EDG_INT2_MASKB_1_ADDR 0x83 +#define IO_NEW_AVI_INFO_MB2_BMSK 0x0001 +#define IO_NEW_AVI_INFO_MB2_SHFT 0 + +#define IO_HDMI_EDG_INT_MASKB_1_ADDR 0x84 +#define IO_NEW_AVI_INFO_MB1_BMSK 0x0001 +#define IO_NEW_AVI_INFO_MB1_SHFT 0 + +#define IO_HDMI_EDG_INT_CLEAR_2_ADDR 0x87 +#define IO_HDMI_EDG_INT_CLEAR_3_ADDR 0x8C + +#define IO_REG_PAD_CTRL_1_ADDR 0x1D +#define IO_PDN_INT1_BMSK 0x0080 +#define IO_PDN_INT1_SHFT 7 +#define IO_PDN_INT2_BMSK 0x0040 +#define IO_PDN_INT2_SHFT 6 +#define IO_PDN_INT3_BMSK 0x0020 +#define IO_PDN_INT3_SHFT 5 +#define IO_INV_LLC_BMSK 0x0010 +#define IO_INV_LLC_SHFT 4 +#define IO_DRV_LLC_PAD_BMSK 0x000C +#define IO_DRV_LLC_PAD_SHFT 2 + +#define IO_REG_INT_RAW_STATUS_ADDR 0x3F +#define IO_INT_CEC_ST_BMSK 0x0010 +#define IO_INT_CEC_ST_SHFT 4 +#define IO_INT_HDMI_ST_BMSK 0x0008 +#define IO_INT_HDMI_ST_SHFT 3 +#define IO_INTRQ3_RAW_BMSK 0x0004 +#define IO_INTRQ3_RAW_SHFT 2 +#define IO_INTRQ2_RAW_BMSK 0x0002 +#define IO_INTRQ2_RAW_SHFT 1 +#define IO_INTRQ1_RAW_BMSK 0x0001 +#define IO_INTRQ1_RAW_SHFT 0 + +#define IO_REG_INT1_CONF_ADDR 0x40 +#define IO_INTRQ_DUR_SEL_BMSK 0x00C0 +#define IO_INTRQ_DUR_SEL_SHFT 6 +#define IO_INTRQ_OP_SEL_BMSK 0x0003 +#define IO_INTRQ_OP_SEL_SHFT 0 + +#define IO_REG_INT2_CONF_ADDR 0x41 +#define IO_INTRQ2_DUR_SEL_BMSK 0x00C0 +#define IO_INTRQ2_DUR_SEL_SHFT 6 +#define IO_CP_LOCK_UNLOCK_EDGE_SEL_BMSK 0x0020 +#define IO_CP_LOCK_UNLOCK_EDGE_SEL_SHFT 5 +#define IO_EN_UMASK_RAW_INTRQ2_BMSK 0x0008 +#define IO_EN_UMASK_RAW_INTRQ2_SHFT 3 +#define IO_INT2_EN_BMSK 0x0004 +#define IO_INT2_EN_SHFT 2 +#define IO_INTRQ2_OP_SEL_BMSK 0x0003 +#define IO_INTRQ2_OP_SEL_SHFT 0 + +#define IO_REG_DATAPATH_RAW_STATUS_ADDR 0x43 +#define IO_CP_LOCK_CP_RAW_BMSK 0x0080 +#define IO_CP_LOCK_CP_RAW_SHFT 7 +#define IO_CP_UNLOCK_CP_RAW_BMSK 0x0040 +#define IO_CP_UNLOCK_CP_RAW_SHFT 6 +#define IO_VMUTE_REQUEST_HDMI_RAW_BMSK 0x0020 +#define IO_VMUTE_REQUEST_HDMI_RAW_SHFT 5 +#define IO_MPU_STIM_INTRQ_RAW_BMSK 0x0002 +#define IO_MPU_STIM_INTRQ_RAW_SHFT 1 +#define IO_INT_SD_RAW_BMSK 0x0001 +#define IO_INT_SD_RAW_SHFT 0 + +#define IO_REG_DATAPATH_INT_STATUS_ADDR 0x44 +#define IO_CP_LOCK_CP_ST_BMSK 0x0080 +#define IO_CP_LOCK_CP_ST_SHFT 7 +#define IO_CP_UNLOCK_CP_ST_BMSK 0x0040 +#define IO_CP_UNLOCK_CP_ST_SHFT 6 +#define IO_VMUTE_REQUEST_HDMI_ST_BMSK 0x0020 +#define IO_VMUTE_REQUEST_HDMI_ST_SHFT 5 +#define IO_MPU_STIM_INTRQ_ST_BMSK 0x0002 +#define IO_MPU_STIM_INTRQ_ST_SHFT 1 +#define IO_INT_SD_ST_BMSK 0x0001 +#define IO_INT_SD_ST_SHFT 0 + +#define IO_REG_DATAPATH_INT_CLEAR_ADDR 0x45 + +#define IO_REG_DATAPATH_INT_MASKB_ADDR 0x47 +#define IO_CP_LOCK_CP_MB1_BMSK 0x0080 +#define IO_CP_LOCK_CP_MB1_SHFT 7 +#define IO_CP_UNLOCK_CP_MB1_BMSK 0x0040 +#define IO_CP_UNLOCK_CP_MB1_SHFT 6 +#define IO_VMUTE_REQUEST_HDMI_MB1_BMSK 0x0020 +#define IO_VMUTE_REQUEST_HDMI_MB1_SHFT 5 +#define IO_MPU_STIM_INTRQ_MB1_BMSK 0x0002 +#define IO_MPU_STIM_INTRQ_MB1_SHFT 1 +#define IO_INT_SD_MB1_BMSK 0x0001 +#define IO_INT_SD_MB1_SHFT 0 + +#define IO_REG_CHIP_REV_ID_1_ADDR 0xDF +#define IO_REG_CHIP_REV_ID_2_ADDR 0xE0 /* Offsets */ -#define IO_REG_DPLL_ADDR 0xF3 -#define IO_REG_CP_ADDR 0xF4 -#define IO_REG_HDMI_ADDR 0xF5 -#define IO_REG_EDID_ADDR 0xF6 -#define IO_REG_HDMI_REP_ADDR 0xF7 -#define IO_REG_HDMI_INF_ADDR 0xF8 -#define IO_REG_CBUS_ADDR 0xF9 -#define IO_REG_CEC_ADDR 0xFA -#define IO_REG_SDP_ADDR 0xFB -#define IO_REG_CSI_TXB_ADDR 0xFC -#define IO_REG_CSI_TXA_ADDR 0xFD +#define IO_REG_DPLL_ADDR 0xF3 +#define IO_REG_CP_ADDR 0xF4 +#define IO_REG_HDMI_ADDR 0xF5 +#define IO_REG_EDID_ADDR 0xF6 +#define IO_REG_HDMI_REP_ADDR 0xF7 +#define IO_REG_HDMI_INF_ADDR 0xF8 +#define IO_REG_CBUS_ADDR 0xF9 +#define IO_REG_CEC_ADDR 0xFA +#define IO_REG_SDP_ADDR 0xFB +#define IO_REG_CSI_TXB_ADDR 0xFC +#define IO_REG_CSI_TXA_ADDR 0xFD /* Sub Address Map Locations */ -#define IO_REG_DPLL_SADDR 0x4C -#define IO_REG_CP_SADDR 0x44 -#define IO_REG_HDMI_SADDR 0x74 -#define IO_REG_EDID_SADDR 0x78 -#define IO_REG_HDMI_REP_SADDR 0x64 -#define IO_REG_HDMI_INF_SADDR 0x62 -#define IO_REG_CBUS_SADDR 0xF0 -#define IO_REG_CEC_SADDR 0x82 -#define IO_REG_SDP_SADDR 0xF2 -#define IO_REG_CSI_TXB_SADDR 0x90 -#define IO_REG_CSI_TXA_SADDR 0x94 +#define IO_REG_DPLL_SADDR 0x4C +#define IO_REG_CP_SADDR 0x44 +#define IO_REG_HDMI_SADDR 0x74 +#define IO_REG_EDID_SADDR 0x78 +#define IO_REG_HDMI_REP_SADDR 0x64 +#define IO_REG_HDMI_INF_SADDR 0x62 +#define IO_REG_CBUS_SADDR 0xF0 +#define IO_REG_CEC_SADDR 0x82 +#define IO_REG_SDP_SADDR 0xF2 +#define IO_REG_CSI_TXB_SADDR 0x90 +#define IO_REG_CSI_TXA_SADDR 0x94 /* HDMI Map Registers */ -#define HDMI_REG_HDMI_PARAM4_ADDR 0x04 -#define HDMI_REG_AV_MUTE_BMSK 0x0040 -#define HDMI_REG_AV_MUTE_SHFT 6 -#define HDMI_REG_TMDS_PLL_LOCKED_BMSK 0x0002 -#define HDMI_REG_TMDS_PLL_LOCKED_SHFT 1 -#define HDMI_REG_AUDIO_PLL_LOCKED_BMSK 0x0001 -#define HDMI_REG_AUDIO_PLL_LOCKED_SHFT 0 - -#define HDMI_REG_HDMI_PARAM5_ADDR 0x05 -#define HDMI_REG_HDMI_MODE_BMSK 0x0080 -#define HDMI_REG_TMDS_FREQ_0_SHFT 7 -#define HDMI_REG_HDMI_CONT_ENCRYPT_BMSK 0x0040 -#define HDMI_REG_HDMI_CONT_ENCRYPT_SHFT 6 -#define HDMI_REG_DVI_HSYNC_POLARITY_BMSK 0x0020 -#define HDMI_REG_DVI_HSYNC_POLARITY_SHFT 5 -#define HDMI_REG_DVI_VSYNC_POLARITY_BMSK 0x0010 -#define HDMI_REG_DVI_VSYNC_POLARITY_SHFT 4 -#define HDMI_REG_PIXEL_REPETITION_BMSK 0x000F -#define HDMI_REG_PIXEL_REPETITION_SHFT 0 - -#define HDMI_REG_LINE_WIDTH_1_ADDR 0x07 -#define HDMI_VERT_FILTER_LOCKED_BMSK 0x0080 -#define HDMI_VERT_FILTER_LOCKED_SHFT 7 -#define HDMI_AUDIO_CHANNEL_MODE_BMSK 0x0040 -#define HDMI_AUDIO_CHANNEL_MODE_SHFT 6 -#define HDMI_DE_REGEN_FILTER_LCK_BMSK 0x0020 -#define HDMI_DE_REGEN_FILTER_LCK_SHFT 5 -#define HDMI_REG_LINE_WIDTH_1_BMSK 0x001F -#define HDMI_REG_LINE_WIDTH_1_SHFT 0 - -#define HDMI_REG_LINE_WIDTH_2_ADDR 0x08 -#define HDMI_REG_LINE_WIDTH_2_BMSK 0x00FF -#define HDMI_REG_LINE_WIDTH_2_SHFT 0 - -#define HDMI_REG_FIELD0_HEIGHT_1_ADDR 0x09 -#define HDMI_REG_FIELD0_HEIGHT_1_BMSK 0x001F -#define HDMI_REG_FIELD0_HEIGHT_1_SHFT 0 -#define HDMI_REG_FIELD0_HEIGHT_2_ADDR 0x0A -#define HDMI_REG_FIELD0_HEIGHT_2_BMSK 0x00FF -#define HDMI_REG_FIELD0_HEIGHT_2_SHFT 0 - -#define HDMI_REG_FIELD1_HEIGHT1_ADDR 0x0B -#define HDMI_REG_DEEP_COLOR_MODE_BMSK 0x00C0 -#define HDMI_REG_DEEP_COLOR_MODE_SHFT 6 -#define HDMI_REG_HDMI_INTERLACED_BMSK 0x0020 -#define HDMI_REG_HDMI_INTERLACED_SHFT 5 - -#define HDMI_REG_TOTAL_LINE_WIDTH_1_ADDR 0x1E -#define HDMI_REG_TOTAL_LINE_WIDTH_1_BMSK 0x003F -#define HDMI_REG_TOTAL_LINE_WIDTH_1_SHFT 0 - -#define HDMI_REG_TOTAL_LINE_WIDTH_2_ADDR 0x1F -#define HDMI_REG_TOTAL_LINE_WIDTH_2_BMSK 0x00FF -#define HDMI_REG_TOTAL_LINE_WIDTH_2_SHFT 0 - -#define HDMI_REG_FIELD0_TOTAL_HEIGHT_1_ADDR 0x26 -#define HDMI_REG_FIELD0_TOT_HEIGHT_1_BMSK 0x003F -#define HDMI_REG_FIELD0_TOT_HEIGHT_1_SHFT 0 - -#define HDMI_REG_FIELD0_TOTAL_HEIGHT_2_ADDR 0x27 -#define HDMI_REG_FIELD0_TOT_HEIGHT_2_BMSK 0x00FF -#define HDMI_REG_FIELD0_TOT_HEIGHT_2_SHFT 0 - -#define HDMI_REG_DIS_CABLE_DET_RST_ADDR 0x48 -#define HDMI_DIS_CABLE_DET_RST_BMSK 0x0040 -#define HDMI_DIS_CABLE_DET_RST_SHFT 6 - -#define HDMI_REG_TMDS_FREQ_ADDR 0x51 -#define HDMI_REG_TMDS_FREQ_BMSK 0x00FF -#define HDMI_REG_TMDS_FREQ_SHFT 0 - -#define HDMI_REG_TMDS_FREQ_FRAC_ADDR 0x52 -#define HDMI_REG_TMDS_FREQ_0_BMSK 0x0080 -#define HDMI_REG_TMDS_FREQ_0_SHFT 7 -#define HDMI_REG_TMDS_FREQ_FRAC_BMSK 0x007F -#define HDMI_REG_TMDS_FREQ_FRAC_SHFT 0 - -#define HDMI_REG_RST_CTRLS_ADDR 0x5A -#define HDMI_HDCP_REPT_EDID_RST_BMSK 0x0008 -#define HDMI_HDCP_REPT_EDID_RST_SHFT 3 - -#define HDMI_REG_MUX_SPDIF_TO_I2S_ADDR 0x6E -#define HDMI_MUX_SPDIF_TO_I2S_EN_BMSK 0x0008 -#define HDMI_MUX_SPDIF_TO_I2S_EN_SHFT 3 +#define HDMI_REG_HDMI_PARAM4_ADDR 0x04 +#define HDMI_REG_AV_MUTE_BMSK 0x0040 +#define HDMI_REG_AV_MUTE_SHFT 6 +#define HDMI_REG_TMDS_PLL_LOCKED_BMSK 0x0002 +#define HDMI_REG_TMDS_PLL_LOCKED_SHFT 1 +#define HDMI_REG_AUDIO_PLL_LOCKED_BMSK 0x0001 +#define HDMI_REG_AUDIO_PLL_LOCKED_SHFT 0 + +#define HDMI_REG_HDMI_PARAM5_ADDR 0x05 +#define HDMI_REG_HDMI_MODE_BMSK 0x0080 +#define HDMI_REG_TMDS_FREQ_0_SHFT 7 +#define HDMI_REG_HDMI_CONT_ENCRYPT_BMSK 0x0040 +#define HDMI_REG_HDMI_CONT_ENCRYPT_SHFT 6 +#define HDMI_REG_DVI_HSYNC_POLARITY_BMSK 0x0020 +#define HDMI_REG_DVI_HSYNC_POLARITY_SHFT 5 +#define HDMI_REG_DVI_VSYNC_POLARITY_BMSK 0x0010 +#define HDMI_REG_DVI_VSYNC_POLARITY_SHFT 4 +#define HDMI_REG_PIXEL_REPETITION_BMSK 0x000F +#define HDMI_REG_PIXEL_REPETITION_SHFT 0 + +#define HDMI_REG_LINE_WIDTH_1_ADDR 0x07 +#define HDMI_VERT_FILTER_LOCKED_BMSK 0x0080 +#define HDMI_VERT_FILTER_LOCKED_SHFT 7 +#define HDMI_AUDIO_CHANNEL_MODE_BMSK 0x0040 +#define HDMI_AUDIO_CHANNEL_MODE_SHFT 6 +#define HDMI_DE_REGEN_FILTER_LCK_BMSK 0x0020 +#define HDMI_DE_REGEN_FILTER_LCK_SHFT 5 +#define HDMI_REG_LINE_WIDTH_1_BMSK 0x001F +#define HDMI_REG_LINE_WIDTH_1_SHFT 0 + +#define HDMI_REG_LINE_WIDTH_2_ADDR 0x08 +#define HDMI_REG_LINE_WIDTH_2_BMSK 0x00FF +#define HDMI_REG_LINE_WIDTH_2_SHFT 0 + +#define HDMI_REG_FIELD0_HEIGHT_1_ADDR 0x09 +#define HDMI_REG_FIELD0_HEIGHT_1_BMSK 0x001F +#define HDMI_REG_FIELD0_HEIGHT_1_SHFT 0 +#define HDMI_REG_FIELD0_HEIGHT_2_ADDR 0x0A +#define HDMI_REG_FIELD0_HEIGHT_2_BMSK 0x00FF +#define HDMI_REG_FIELD0_HEIGHT_2_SHFT 0 + +#define HDMI_REG_FIELD1_HEIGHT1_ADDR 0x0B +#define HDMI_REG_DEEP_COLOR_MODE_BMSK 0x00C0 +#define HDMI_REG_DEEP_COLOR_MODE_SHFT 6 +#define HDMI_REG_HDMI_INTERLACED_BMSK 0x0020 +#define HDMI_REG_HDMI_INTERLACED_SHFT 5 + +#define HDMI_REG_TOTAL_LINE_WIDTH_1_ADDR 0x1E +#define HDMI_REG_TOTAL_LINE_WIDTH_1_BMSK 0x003F +#define HDMI_REG_TOTAL_LINE_WIDTH_1_SHFT 0 + +#define HDMI_REG_TOTAL_LINE_WIDTH_2_ADDR 0x1F +#define HDMI_REG_TOTAL_LINE_WIDTH_2_BMSK 0x00FF +#define HDMI_REG_TOTAL_LINE_WIDTH_2_SHFT 0 + +#define HDMI_REG_FIELD0_TOTAL_HEIGHT_1_ADDR 0x26 +#define HDMI_REG_FIELD0_TOT_HEIGHT_1_BMSK 0x003F +#define HDMI_REG_FIELD0_TOT_HEIGHT_1_SHFT 0 + +#define HDMI_REG_FIELD0_TOTAL_HEIGHT_2_ADDR 0x27 +#define HDMI_REG_FIELD0_TOT_HEIGHT_2_BMSK 0x00FF +#define HDMI_REG_FIELD0_TOT_HEIGHT_2_SHFT 0 + +#define HDMI_REG_DIS_CABLE_DET_RST_ADDR 0x48 +#define HDMI_DIS_CABLE_DET_RST_BMSK 0x0040 +#define HDMI_DIS_CABLE_DET_RST_SHFT 6 + +#define HDMI_REG_TMDS_FREQ_ADDR 0x51 +#define HDMI_REG_TMDS_FREQ_BMSK 0x00FF +#define HDMI_REG_TMDS_FREQ_SHFT 0 + +#define HDMI_REG_TMDS_FREQ_FRAC_ADDR 0x52 +#define HDMI_REG_TMDS_FREQ_0_BMSK 0x0080 +#define HDMI_REG_TMDS_FREQ_0_SHFT 7 +#define HDMI_REG_TMDS_FREQ_FRAC_BMSK 0x007F +#define HDMI_REG_TMDS_FREQ_FRAC_SHFT 0 + +#define HDMI_REG_RST_CTRLS_ADDR 0x5A +#define HDMI_HDCP_REPT_EDID_RST_BMSK 0x0008 +#define HDMI_HDCP_REPT_EDID_RST_SHFT 3 + +#define HDMI_REG_MUX_SPDIF_TO_I2S_ADDR 0x6E +#define HDMI_MUX_SPDIF_TO_I2S_EN_BMSK 0x0008 +#define HDMI_MUX_SPDIF_TO_I2S_EN_SHFT 3 /* HDMI Repeater Map Registers */ -#define HDMI_REG_HDCP_EDID_CTRLS_ADDR 0x74 -#define HDMI_MAN_EDID_A_ENABLE_BMSK 0x0001 -#define HDMI_MAN_EDID_A_ENABLE_SHFT 0 +#define HDMI_REG_HDCP_EDID_CTRLS_ADDR 0x74 +#define HDMI_MAN_EDID_A_ENABLE_BMSK 0x0001 +#define HDMI_MAN_EDID_A_ENABLE_SHFT 0 + +#define HDMI_REG_RO_EDID_DEBUG_2_ADDR 0x76 +#define HDMI_EDID_A_ENABLE_BMSK 0x0001 +#define HDMI_EDID_A_ENABLE_SHFT 0 + +/* CEC Map Registers */ +#define CEC_REG_LOG_ADDR_MASK_ADDR 0x27 +#define CEC_REG_LOG_ADDR_MASK2_BMSK 0x0040 +#define CEC_REG_LOG_ADDR_MASK2_SHFT 6 +#define CEC_REG_LOG_ADDR_MASK1_BMSK 0x0020 +#define CEC_REG_LOG_ADDR_MASK1_SHFT 5 +#define CEC_REG_LOG_ADDR_MASK0_BMSK 0x0010 +#define CEC_REG_LOG_ADDR_MASK0_SHFT 4 +#define CEC_REG_ERROR_REPORT_MODE_BMSK 0x0008 +#define CEC_REG_ERROR_REPORT_MODE_SHFT 3 +#define CEC_REG_ERROR_REPORT_DET_BMSK 0x0004 +#define CEC_REG_ERROR_REPORT_DET_SHFT 2 +#define CEC_REG_FORCE_NACK_BMSK 0x0002 +#define CEC_REG_FORCE_NACK_SHFT 1 +#define CEC_REG_FORCE_IGNORE_BMSK 0x0001 +#define CEC_REG_FORCE_IGNORE_SHFT 0 + +#define CEC_REG_LOGICAL_ADDRESS0_1_ADDR 0x28 +#define CEC_REG_LOGICAL_ADDRESS1_BMSK 0x00F0 +#define CEC_REG_LOGICAL_ADDRESS1_SHFT 4 +#define CEC_REG_LOGICAL_ADDRESS0_BMSK 0x000F +#define CEC_REG_LOGICAL_ADDRESS0_SHFT 0 + +#define CEC_REG_LOGICAL_ADDRESS2_ADDR 0x29 +#define CEC_REG_LOGICAL_ADDRESS2_BMSK 0x000F +#define CEC_REG_LOGICAL_ADDRESS2_SHFT 0 + +#define CEC_REG_CEC_POWER_UP_ADDR 0x2A +#define CEC_REG_CEC_POWER_UP_BMSK 0x0001 +#define CEC_REG_CEC_POWER_UP_SHFT 0 + +#define CEC_REG_CLR_RX_RDY_SFT_RST_ADDR 0x2C +#define CEC_REG_CEC_SOFT_RESET_BMSK 0x0001 +#define CEC_REG_CEC_SOFT_RESET_SHFT 0 -#define HDMI_REG_RO_EDID_DEBUG_2_ADDR 0x76 -#define HDMI_EDID_A_ENABLE_BMSK 0x0001 -#define HDMI_EDID_A_ENABLE_SHFT 0 /* CP Map Registers */ -#define CP_REG_CONTRAST 0x3A -#define CP_REG_SATURATION 0x3B -#define CP_REG_BRIGHTNESS 0x3C -#define CP_REG_HUE 0x3D -#define CP_REG_VID_ADJ 0x3E -#define CP_CTR_VID_ADJ_EN 0x80 -#define CP_REG_STDI_CH_ADDR 0xB1 -#define CP_STDI_DVALID_CH1_BMSK 0x0080 -#define CP_STDI_DVALID_CH1_SHFT 7 +#define CP_REG_CONTRAST 0x3A +#define CP_REG_SATURATION 0x3B +#define CP_REG_BRIGHTNESS 0x3C +#define CP_REG_HUE 0x3D +#define CP_REG_VID_ADJ 0x3E +#define CP_CTR_VID_ADJ_EN 0x80 +#define CP_REG_STDI_CH_ADDR 0xB1 +#define CP_STDI_DVALID_CH1_BMSK 0x0080 +#define CP_STDI_DVALID_CH1_SHFT 7 + +/* SDP Main Map */ +#define SDP_RW_MAP_REG 0x0e + +/* SDP MAP 1 Registers */ +#define SDP_RW_LOCK_UNLOCK_CLR_ADDR 0x43 +#define SDP_RW_LOCK_UNLOCK_MASK_ADDR 0x44 /* SDP R/O Main Map Registers */ -#define SDP_RO_MAIN_STATUS1_ADDR 0x10 -#define SDP_RO_MAIN_COL_KILL_BMSK 0x0080 -#define SDP_RO_MAIN_COL_KILL_SHFT 7 -#define SDP_RO_MAIN_AD_RESULT_BMSK 0x0070 -#define SDP_RO_MAIN_AD_RESULT_SHFT 4 -#define SDP_RO_MAIN_FOLLOW_PW_BMSK 0x0008 -#define SDP_RO_MAIN_FOLLOW_PW_SHFT 3 -#define SDP_RO_MAIN_FSC_LOCK_BMSK 0x0004 -#define SDP_RO_MAIN_FSC_LOCK_SHFT 2 -#define SDP_RO_MAIN_LOST_LOCK_BMSK 0x0002 -#define SDP_RO_MAIN_LOST_LOCK_SHFT 1 -#define SDP_RO_MAIN_IN_LOCK_BMSK 0x0001 -#define SDP_RO_MAIN_IN_LOCK_SHFT 0 +#define SDP_RO_MAIN_STATUS1_ADDR 0x10 +#define SDP_RO_MAIN_COL_KILL_BMSK 0x0080 +#define SDP_RO_MAIN_COL_KILL_SHFT 7 +#define SDP_RO_MAIN_AD_RESULT_BMSK 0x0070 +#define SDP_RO_MAIN_AD_RESULT_SHFT 4 +#define SDP_RO_MAIN_FOLLOW_PW_BMSK 0x0008 +#define SDP_RO_MAIN_FOLLOW_PW_SHFT 3 +#define SDP_RO_MAIN_FSC_LOCK_BMSK 0x0004 +#define SDP_RO_MAIN_FSC_LOCK_SHFT 2 +#define SDP_RO_MAIN_LOST_LOCK_BMSK 0x0002 +#define SDP_RO_MAIN_LOST_LOCK_SHFT 1 +#define SDP_RO_MAIN_IN_LOCK_BMSK 0x0001 +#define SDP_RO_MAIN_IN_LOCK_SHFT 0 + /* * CSI Map Registers */ -#define CSI_REG_TX_CFG1_ADDR 0x00 -#define CSI_CTRL_TX_PWRDN_BMSK 0x0080 -#define CSI_CTRL_TX_PWRDN_SHFT 7 -#define CSI_CTRL_AUTO_PARAMS_BMSK 0x0020 -#define CSI_CTRL_AUTO_PARAMS_SHFT 5 -#define CSI_CTRL_NUM_LANES_BMSK 0x0007 -#define CSI_CTRL_NUM_LANES_SHFT 0 - -#define CSI_REG_TX_DPHY_PWDN_ADDR 0xF0 -#define CSI_CTRL_DPHY_PWDN_BMSK 0x0001 -#define CSI_CTRL_DPHY_PWDN_SHFT 0 +#define CSI_REG_TX_CFG1_ADDR 0x00 +#define CSI_CTRL_TX_PWRDN_BMSK 0x0080 +#define CSI_CTRL_TX_PWRDN_SHFT 7 +#define CSI_CTRL_AUTO_PARAMS_BMSK 0x0020 +#define CSI_CTRL_AUTO_PARAMS_SHFT 5 +#define CSI_CTRL_NUM_LANES_BMSK 0x0007 +#define CSI_CTRL_NUM_LANES_SHFT 0 + +#define CSI_REG_TX_DPHY_PWDN_ADDR 0xF0 +#define CSI_CTRL_DPHY_PWDN_BMSK 0x0001 +#define CSI_CTRL_DPHY_PWDN_SHFT 0 enum adv7481_adresult { - AD_NTSM_M_J = 0x0, - AD_NTSC_4_43 = 0x1, - AD_PAL_M = 0x2, - AD_PAL_60 = 0x3, - AD_PAL_B_G = 0x4, - AD_SECAM = 0x5, - AD_PAL_COMB_N = 0x6, - AD_SECAM_525 = 0x7, + AD_NTSM_M_J = 0x0, + AD_NTSC_4_43 = 0x1, + AD_PAL_M = 0x2, + AD_PAL_60 = 0x3, + AD_PAL_B_G = 0x4, + AD_SECAM = 0x5, + AD_PAL_COMB_N = 0x6, + AD_SECAM_525 = 0x7, }; enum adv7481_color_depth { - CD_8BIT = 0x0, - CD_10BIT = 0x1, - CD_12BIT = 0x2, - CD_16BIT = 0x3, + CD_8BIT = 0x0, + CD_10BIT = 0x1, + CD_12BIT = 0x2, + CD_16BIT = 0x3, }; enum adv7481_intrq_dur_sel { - AD_4_XTAL_PER = 0x0, - AD_16_XTAL_PER = 0x1, - AD_64_XTAL_PER = 0x2, - AD_ACTIVE_UNTIL_CLR = 0x3, + AD_4_XTAL_PER = 0x0, + AD_16_XTAL_PER = 0x1, + AD_64_XTAL_PER = 0x2, + AD_ACTIVE_UNTIL_CLR = 0x3, }; enum adv7481_intrq_op_sel { - AD_OP_OPEN_DRAIN = 0x0, - AD_OP_DRIVE_LOW = 0x1, - AD_OP_DRIVE_HIGH = 0x2, - AD_OP_DISABLED = 0x3, + AD_OP_OPEN_DRAIN = 0x0, + AD_OP_DRIVE_LOW = 0x1, + AD_OP_DRIVE_HIGH = 0x2, + AD_OP_DISABLED = 0x3, }; enum adv7481_drv_llc_pad { - AD_LLC_PAD_NOT_USED = 0x0, - AD_MIN_DRIVE_STRNGTH = 0x1, - AD_MID_DRIVE_STRNGTH = 0x2, - AD_MAX_DRIVE_STRNGTH = 0x3, + AD_LLC_PAD_NOT_USED = 0x0, + AD_MIN_DRIVE_STRNGTH = 0x1, + AD_MID_DRIVE_STRNGTH = 0x2, + AD_MAX_DRIVE_STRNGTH = 0x3, }; #endif diff --git a/drivers/media/platform/msm/ais/camera/camera.c b/drivers/media/platform/msm/ais/camera/camera.c index 158b83c12d00..33808d18d4c4 100644 --- a/drivers/media/platform/msm/ais/camera/camera.c +++ b/drivers/media/platform/msm/ais/camera/camera.c @@ -491,13 +491,16 @@ static long camera_v4l2_vidioc_private_ioctl(struct file *filep, void *fh, if (WARN_ON(!k_ioctl || !pvdev)) return -EIO; + if (cmd != VIDIOC_MSM_CAMERA_PRIVATE_IOCTL_CMD) + return -EINVAL; + switch (k_ioctl->id) { case MSM_CAMERA_PRIV_IOCTL_ID_RETURN_BUF: { struct msm_camera_return_buf ptr, *tmp = NULL; MSM_CAM_GET_IOCTL_ARG_PTR(&tmp, &k_ioctl->ioctl_ptr, sizeof(tmp)); - if (copy_from_user(&ptr, tmp, + if (copy_from_user(&ptr, (void __user *)tmp, sizeof(struct msm_camera_return_buf))) { return -EFAULT; } @@ -795,7 +798,7 @@ static long camera_handle_internal_compat_ioctl(struct file *file, { long rc = 0; struct msm_camera_private_ioctl_arg k_ioctl; - void __user *tmp_compat_ioctl_ptr = NULL; + void *tmp_compat_ioctl_ptr = NULL; rc = msm_copy_camera_private_ioctl_args(arg, &k_ioctl, &tmp_compat_ioctl_ptr); @@ -810,11 +813,13 @@ static long camera_handle_internal_compat_ioctl(struct file *file, k_ioctl.id, k_ioctl.size); return -EINVAL; } - k_ioctl.ioctl_ptr = (__u64)tmp_compat_ioctl_ptr; - if (!k_ioctl.ioctl_ptr) { + + if (tmp_compat_ioctl_ptr == NULL) { pr_debug("Invalid ptr for id %d", k_ioctl.id); return -EINVAL; } + k_ioctl.ioctl_ptr = (__u64)(uintptr_t)tmp_compat_ioctl_ptr; + rc = camera_v4l2_vidioc_private_ioctl(file, file->private_data, 0, cmd, (void *)&k_ioctl); } @@ -826,7 +831,7 @@ static long camera_handle_internal_compat_ioctl(struct file *file, return rc; } -long camera_v4l2_compat_ioctl(struct file *file, unsigned int cmd, +static long camera_v4l2_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret = 0; diff --git a/drivers/media/platform/msm/ais/common/cam_hw_ops.c b/drivers/media/platform/msm/ais/common/cam_hw_ops.c index 073778c9edcc..cf28e0ca6536 100644 --- a/drivers/media/platform/msm/ais/common/cam_hw_ops.c +++ b/drivers/media/platform/msm/ais/common/cam_hw_ops.c @@ -50,7 +50,7 @@ struct cam_ahb_client_data { static struct cam_ahb_client_data data; -int get_vector_index(char *name) +static int get_vector_index(char *name) { int i = 0, rc = -1; @@ -213,7 +213,7 @@ err1: } EXPORT_SYMBOL(cam_ahb_clk_init); -int cam_consolidate_ahb_vote(enum cam_ahb_clk_client id, +static int cam_consolidate_ahb_vote(enum cam_ahb_clk_client id, enum cam_ahb_clk_vote vote) { int i = 0; diff --git a/drivers/media/platform/msm/ais/common/cam_smmu_api.c b/drivers/media/platform/msm/ais/common/cam_smmu_api.c index d3b239e9f304..0f0e14506325 100644 --- a/drivers/media/platform/msm/ais/common/cam_smmu_api.c +++ b/drivers/media/platform/msm/ais/common/cam_smmu_api.c @@ -466,7 +466,7 @@ static enum dma_data_direction cam_smmu_translate_dir( return DMA_NONE; } -void cam_smmu_reset_iommu_table(enum cam_smmu_init_dir ops) +static void cam_smmu_reset_iommu_table(enum cam_smmu_init_dir ops) { unsigned int i; int j = 0; diff --git a/drivers/media/platform/msm/ais/common/cam_smmu_api.h b/drivers/media/platform/msm/ais/common/cam_smmu_api.h index 4a13598dc719..26bd30a6c8c8 100644 --- a/drivers/media/platform/msm/ais/common/cam_smmu_api.h +++ b/drivers/media/platform/msm/ais/common/cam_smmu_api.h @@ -43,6 +43,10 @@ enum cam_smmu_map_dir { CAM_SMMU_MAP_INVALID }; +int cam_smmu_query_vaddr_in_range(int handle, + unsigned long fault_addr, unsigned long *start_addr, + unsigned long *end_addr, int *fd); + /** * @param identifier: Unique identifier to be used by clients which they * should get from device tree. CAM SMMU driver will diff --git a/drivers/media/platform/msm/ais/common/cam_soc_api.c b/drivers/media/platform/msm/ais/common/cam_soc_api.c index 118d665a44d3..92f3e4007390 100644 --- a/drivers/media/platform/msm/ais/common/cam_soc_api.c +++ b/drivers/media/platform/msm/ais/common/cam_soc_api.c @@ -36,7 +36,7 @@ struct msm_cam_bus_pscale_data { struct mutex lock; }; -struct msm_cam_bus_pscale_data g_cv[CAM_BUS_CLIENT_MAX]; +static struct msm_cam_bus_pscale_data g_cv[CAM_BUS_CLIENT_MAX]; /* Get all clocks from DT */ static int msm_camera_get_clk_info_internal(struct device *dev, @@ -771,7 +771,7 @@ void __iomem *msm_camera_get_reg_base(struct platform_device *pdev, char *device_name, int reserve_mem) { struct resource *mem; - void *base; + void __iomem *base; if (!pdev || !device_name) { pr_err("Invalid params\n"); diff --git a/drivers/media/platform/msm/ais/common/msm_camera_io_util.c b/drivers/media/platform/msm/ais/common/msm_camera_io_util.c index 8370f556a40d..22518c2cae7d 100644 --- a/drivers/media/platform/msm/ais/common/msm_camera_io_util.c +++ b/drivers/media/platform/msm/ais/common/msm_camera_io_util.c @@ -123,8 +123,8 @@ void msm_camera_io_memcpy_toio(void __iomem *dest_addr, void __iomem *src_addr, u32 len) { int i; - u32 *d = (u32 *) dest_addr; - u32 *s = (u32 *) src_addr; + u32 __iomem *d = (u32 __iomem *) dest_addr; + u32 __iomem *s = (u32 __iomem *) src_addr; for (i = 0; i < len; i++) writel_relaxed(*s++, d++); @@ -178,7 +178,7 @@ void msm_camera_io_dump(void __iomem *addr, int size, int enable) { char line_str[128], *p_str; int i; - u32 *p = (u32 *) addr; + u32 __iomem *p = (u32 __iomem *) addr; u32 data; CDBG("%s: addr=%pK size=%d\n", __func__, addr, size); @@ -242,8 +242,8 @@ void msm_camera_io_memcpy_mb(void __iomem *dest_addr, void __iomem *src_addr, u32 len) { int i; - u32 *d = (u32 *) dest_addr; - u32 *s = (u32 *) src_addr; + u32 __iomem *d = (u32 __iomem *) dest_addr; + u32 __iomem *s = (u32 __iomem *) src_addr; /* This is generic function called who needs to register * writes with memory barrier */ diff --git a/drivers/media/platform/msm/ais/common/msm_camera_io_util.h b/drivers/media/platform/msm/ais/common/msm_camera_io_util.h index 338e24d45500..3bd6c5f4866e 100644 --- a/drivers/media/platform/msm/ais/common/msm_camera_io_util.h +++ b/drivers/media/platform/msm/ais/common/msm_camera_io_util.h @@ -40,6 +40,8 @@ void msm_camera_io_w(u32 data, void __iomem *addr); void msm_camera_io_w_mb(u32 data, void __iomem *addr); u32 msm_camera_io_r(void __iomem *addr); u32 msm_camera_io_r_mb(void __iomem *addr); +void msm_camera_io_memcpy_toio(void __iomem *dest_addr, + void __iomem *src_addr, u32 len); void msm_camera_io_dump(void __iomem *addr, int size, int enable); void msm_camera_io_memcpy(void __iomem *dest_addr, void __iomem *src_addr, u32 len); diff --git a/drivers/media/platform/msm/ais/fd/msm_fd_dev.c b/drivers/media/platform/msm/ais/fd/msm_fd_dev.c index 7a4acf6ec815..420083f019cf 100644 --- a/drivers/media/platform/msm/ais/fd/msm_fd_dev.c +++ b/drivers/media/platform/msm/ais/fd/msm_fd_dev.c @@ -745,9 +745,13 @@ static int msm_fd_s_fmt_vid_out(struct file *file, static int msm_fd_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req) { + int ret; struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); - return vb2_reqbufs(&ctx->vb2_q, req); + mutex_lock(&ctx->fd_device->recovery_lock); + ret = vb2_reqbufs(&ctx->vb2_q, req); + mutex_unlock(&ctx->fd_device->recovery_lock); + return ret; } /* @@ -759,9 +763,14 @@ static int msm_fd_reqbufs(struct file *file, static int msm_fd_qbuf(struct file *file, void *fh, struct v4l2_buffer *pb) { + int ret; struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); - return vb2_qbuf(&ctx->vb2_q, pb); + mutex_lock(&ctx->fd_device->recovery_lock); + ret = vb2_qbuf(&ctx->vb2_q, pb); + mutex_unlock(&ctx->fd_device->recovery_lock); + return ret; + } /* @@ -773,9 +782,13 @@ static int msm_fd_qbuf(struct file *file, void *fh, static int msm_fd_dqbuf(struct file *file, void *fh, struct v4l2_buffer *pb) { + int ret; struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); - return vb2_dqbuf(&ctx->vb2_q, pb, file->f_flags & O_NONBLOCK); + mutex_lock(&ctx->fd_device->recovery_lock); + ret = vb2_dqbuf(&ctx->vb2_q, pb, file->f_flags & O_NONBLOCK); + mutex_unlock(&ctx->fd_device->recovery_lock); + return ret; } /* diff --git a/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c b/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c index fcd803d4b138..585865b12387 100644 --- a/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c +++ b/drivers/media/platform/msm/ais/isp/msm_buf_mgr.c @@ -76,7 +76,7 @@ static int msm_buf_check_head_sanity(struct msm_isp_bufq *bufq) return rc; } -struct msm_isp_bufq *msm_isp_get_bufq( +static struct msm_isp_bufq *msm_isp_get_bufq( struct msm_isp_buf_mgr *buf_mgr, uint32_t bufq_handle) { @@ -161,7 +161,7 @@ static int msm_isp_free_bufq_handle(struct msm_isp_buf_mgr *buf_mgr, /* Set everything except lock to 0 */ bufq->bufq_handle = 0; - bufq->bufs = 0; + bufq->bufs = NULL; bufq->vfe_id = 0; bufq->output_id = 0; bufq->num_bufs = 0; @@ -212,8 +212,8 @@ static int msm_isp_prepare_v4l2_buf(struct msm_isp_buf_mgr *buf_mgr, mapped_info->paddr += accu_length; accu_length += qbuf_buf->planes[i].length; - CDBG("%s: plane: %d addr:%lu\n", - __func__, i, (unsigned long)mapped_info->paddr); + CDBG("%s: plane: %d addr:%pK\n", + __func__, i, (void *)mapped_info->paddr); } buf_info->num_planes = qbuf_buf->num_planes; @@ -275,8 +275,8 @@ static int msm_isp_map_buf(struct msm_isp_buf_mgr *buf_mgr, pr_err_ratelimited("%s: cannot map address", __func__); goto smmu_map_error; } - CDBG("%s: addr:%lu\n", - __func__, (unsigned long)mapped_info->paddr); + CDBG("%s: addr:%pK\n", + __func__, (void *)mapped_info->paddr); return rc; smmu_map_error: @@ -1367,14 +1367,15 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr, struct msm_isp_buffer *bufs = NULL; uint32_t i = 0, j = 0, k = 0, rc = 0; char *print_buf = NULL, temp_buf[100]; - uint32_t start_addr = 0, end_addr = 0, print_buf_size = 2000; + uint32_t print_buf_size = 2000; + unsigned long start_addr = 0, end_addr = 0; int buf_addr_delta = -1; int temp_delta = 0; uint32_t debug_output_id = 0; uint32_t debug_buf_idx = 0; uint32_t debug_buf_plane = 0; - uint32_t debug_start_addr = 0; - uint32_t debug_end_addr = 0; + unsigned long debug_start_addr = 0; + unsigned long debug_end_addr = 0; uint32_t debug_frame_id = 0; enum msm_isp_buffer_state debug_state = MSM_ISP_BUFFER_STATE_UNUSED; unsigned long flags; @@ -1433,8 +1434,8 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr, debug_output_id, debug_frame_id); pr_err("%s: nearby buf index %d, plane %d, state %d\n", __func__, debug_buf_idx, debug_buf_plane, debug_state); - pr_err("%s: buf address 0x%x -- 0x%x\n", __func__, - debug_start_addr, debug_end_addr); + pr_err("%s: buf address %pK -- %pK\n", __func__, + (void *)debug_start_addr, (void *)debug_end_addr); if (BUF_DEBUG_FULL) { print_buf = kzalloc(print_buf_size, GFP_ATOMIC); @@ -1469,9 +1470,10 @@ static int msm_isp_buf_mgr_debug(struct msm_isp_buf_mgr *buf_mgr, mapped_info[k].len; snprintf(temp_buf, sizeof(temp_buf), - " buf %d plane %d start_addr %x end_addr %x\n", - j, k, start_addr, - end_addr); + " buf %d plane %d start_addr %pK end_addr %pK\n", + j, k, + (void *)start_addr, + (void *)end_addr); strlcat(print_buf, temp_buf, print_buf_size); } diff --git a/drivers/media/platform/msm/ais/isp/msm_isp.c b/drivers/media/platform/msm/ais/isp/msm_isp.c index 97c0f779cf73..d62b830535a3 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp.c +++ b/drivers/media/platform/msm/ais/isp/msm_isp.c @@ -49,9 +49,6 @@ MODULE_DEVICE_TABLE(of, msm_vfe_dt_match); #define OVERFLOW_BUFFER_LENGTH 64 static char stat_line[OVERFLOW_LENGTH]; -struct msm_isp_statistics stats; -struct msm_isp_ub_info ub_info; - static int msm_isp_enable_debugfs(struct vfe_device *vfe_dev, struct msm_isp_bw_req_info *isp_req_hist); @@ -107,8 +104,8 @@ static int vfe_debugfs_statistics_open(struct inode *inode, struct file *file) return 0; } -static ssize_t vfe_debugfs_statistics_read(struct file *t_file, char *t_char, - size_t t_size_t, loff_t *t_loff_t) +static ssize_t vfe_debugfs_statistics_read(struct file *t_file, + char __user *t_char, size_t t_size_t, loff_t *t_loff_t) { int i; uint64_t *ptr; @@ -132,7 +129,7 @@ static ssize_t vfe_debugfs_statistics_read(struct file *t_file, char *t_char, } static ssize_t vfe_debugfs_statistics_write(struct file *t_file, - const char *t_char, size_t t_size_t, loff_t *t_loff_t) + const char __user *t_char, size_t t_size_t, loff_t *t_loff_t) { struct vfe_device *vfe_dev = (struct vfe_device *) t_file->private_data; @@ -149,7 +146,7 @@ static int bw_history_open(struct inode *inode, struct file *file) return 0; } -static ssize_t bw_history_read(struct file *t_file, char *t_char, +static ssize_t bw_history_read(struct file *t_file, char __user *t_char, size_t t_size_t, loff_t *t_loff_t) { int i; @@ -194,7 +191,7 @@ static ssize_t bw_history_read(struct file *t_file, char *t_char, } static ssize_t bw_history_write(struct file *t_file, - const char *t_char, size_t t_size_t, loff_t *t_loff_t) + const char __user *t_char, size_t t_size_t, loff_t *t_loff_t) { struct msm_isp_bw_req_info *isp_req_hist = (struct msm_isp_bw_req_info *) t_file->private_data; @@ -210,7 +207,7 @@ static int ub_info_open(struct inode *inode, struct file *file) return 0; } -static ssize_t ub_info_read(struct file *t_file, char *t_char, +static ssize_t ub_info_read(struct file *t_file, char __user *t_char, size_t t_size_t, loff_t *t_loff_t) { int i; @@ -241,7 +238,7 @@ static ssize_t ub_info_read(struct file *t_file, char *t_char, } static ssize_t ub_info_write(struct file *t_file, - const char *t_char, size_t t_size_t, loff_t *t_loff_t) + const char __user *t_char, size_t t_size_t, loff_t *t_loff_t) { struct vfe_device *vfe_dev = (struct vfe_device *) t_file->private_data; diff --git a/drivers/media/platform/msm/ais/isp/msm_isp47.c b/drivers/media/platform/msm/ais/isp/msm_isp47.c index 8991433b2c67..d63282f80aca 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp47.c +++ b/drivers/media/platform/msm/ais/isp/msm_isp47.c @@ -2708,7 +2708,6 @@ struct msm_vfe_hardware_info vfe47_hw_info = { .process_camif_irq = msm_vfe47_process_input_irq, .process_reset_irq = msm_vfe47_process_reset_irq, .process_halt_irq = msm_vfe47_process_halt_irq, - .process_reset_irq = msm_vfe47_process_reset_irq, .process_reg_update = msm_vfe47_process_reg_update, .process_axi_irq = msm_isp_process_axi_irq, .process_stats_irq = msm_isp_process_stats_irq, diff --git a/drivers/media/platform/msm/ais/isp/msm_isp47.h b/drivers/media/platform/msm/ais/isp/msm_isp47.h index b29fca61ce7c..9af0acd3656a 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp47.h +++ b/drivers/media/platform/msm/ais/isp/msm_isp47.h @@ -30,6 +30,8 @@ enum msm_vfe47_stats_comp_idx { extern struct msm_vfe_hardware_info vfe47_hw_info; +uint32_t msm_vfe47_ub_reg_offset(struct vfe_device *vfe_dev, int wm_idx); +uint32_t msm_vfe47_get_ub_size(struct vfe_device *vfe_dev); void msm_vfe47_read_irq_status(struct vfe_device *vfe_dev, uint32_t *irq_status0, uint32_t *irq_status1); void msm_vfe47_read_irq_status_and_clear(struct vfe_device *vfe_dev, @@ -70,6 +72,8 @@ int32_t msm_vfe47_cfg_io_format(struct vfe_device *vfe_dev, enum msm_vfe_axi_stream_src stream_src, uint32_t io_format); int msm_vfe47_start_fetch_engine(struct vfe_device *vfe_dev, void *arg); +int msm_vfe47_start_fetch_engine_multi_pass(struct vfe_device *vfe_dev, + void *arg); void msm_vfe47_cfg_fetch_engine(struct vfe_device *vfe_dev, struct msm_vfe_pix_cfg *pix_cfg); void msm_vfe47_cfg_testgen(struct vfe_device *vfe_dev, diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c index 373a963f75aa..a85ee30769c4 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c @@ -3889,6 +3889,12 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) case UPDATE_STREAM_REQUEST_FRAMES_VER2: { struct msm_vfe_axi_stream_cfg_update_info_req_frm *req_frm = &update_cmd->req_frm_ver2; + if (HANDLE_TO_IDX(req_frm->stream_handle) >= VFE_AXI_SRC_MAX) { + pr_err("%s: Invalid stream handle\n", __func__); + rc = -EINVAL; + break; + } + stream_info = &axi_data->stream_info[HANDLE_TO_IDX( req_frm->stream_handle)]; rc = msm_isp_request_frame(vfe_dev, stream_info, diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h index 0396fc4680f1..5ed89161b7f3 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h +++ b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.h @@ -27,6 +27,9 @@ int msm_isp_axi_create_stream(struct vfe_device *vfe_dev, void msm_isp_axi_destroy_stream( struct msm_vfe_axi_shared_data *axi_data, int stream_idx); +int msm_isp_axi_get_num_planes(uint32_t output_format, + struct msm_vfe_axi_stream *stream_info); + int msm_isp_validate_axi_request( struct msm_vfe_axi_shared_data *axi_data, struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd); @@ -36,21 +39,34 @@ void msm_isp_axi_reserve_wm( struct msm_vfe_axi_shared_data *axi_data, struct msm_vfe_axi_stream *stream_info); +void msm_isp_axi_free_wm(struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream *stream_info); + void msm_isp_axi_reserve_comp_mask( struct msm_vfe_axi_shared_data *axi_data, struct msm_vfe_axi_stream *stream_info); +void msm_isp_axi_free_comp_mask(struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream *stream_info); + int msm_isp_axi_check_stream_state( struct vfe_device *vfe_dev, struct msm_vfe_axi_stream_cfg_cmd *stream_cfg_cmd); +void msm_isp_check_for_output_error(struct vfe_device *vfe_dev, + struct msm_isp_timestamp *ts, struct msm_isp_sof_info *sof_info); + int msm_isp_calculate_framedrop( struct msm_vfe_axi_shared_data *axi_data, struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd); +void msm_isp_calculate_bandwidth( + struct msm_vfe_axi_shared_data *axi_data, + struct msm_vfe_axi_stream *stream_info); void msm_isp_reset_framedrop(struct vfe_device *vfe_dev, struct msm_vfe_axi_stream *stream_info); int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg); +void msm_isp_start_avtimer(void); void msm_isp_get_avtimer_ts(struct msm_isp_timestamp *time_stamp); int msm_isp_cfg_axi_stream(struct vfe_device *vfe_dev, void *arg); int msm_isp_update_stream_bandwidth(struct vfe_device *vfe_dev, diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c index 3b877b4cb994..6e89544161ee 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c @@ -863,6 +863,12 @@ int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg) if (vfe_dev->stats_data.num_active_stream == 0) vfe_dev->hw_info->vfe_ops.stats_ops.cfg_ub(vfe_dev); + if (stream_cfg_cmd->num_streams > MSM_ISP_STATS_MAX) { + pr_err("%s invalid num_streams %d\n", __func__, + stream_cfg_cmd->num_streams); + return -EINVAL; + } + if (stream_cfg_cmd->enable) { msm_isp_stats_update_cgc_override(vfe_dev, stream_cfg_cmd); @@ -891,7 +897,7 @@ int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg) &update_cmd->update_info[i]; /* check array reference bounds */ if (STATS_IDX(update_info->stream_handle) - > vfe_dev->hw_info->stats_hw_info->num_stats_type) { + >= vfe_dev->hw_info->stats_hw_info->num_stats_type) { pr_err("%s: stats idx %d out of bound!", __func__, STATS_IDX(update_info->stream_handle)); return -EINVAL; diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.h b/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.h index 707901bc6271..ae438a675542 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.h +++ b/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.h @@ -19,6 +19,8 @@ void msm_isp_process_stats_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, uint32_t pingpong_status, struct msm_isp_timestamp *ts); +int msm_isp_stats_create_stream(struct vfe_device *vfe_dev, + struct msm_vfe_stats_stream_request_cmd *stream_req_cmd); void msm_isp_stats_stream_update(struct vfe_device *vfe_dev); int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg); int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg); diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_util.c index 0353ab27cf19..9e5317eb2920 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/ais/isp/msm_isp_util.c @@ -512,7 +512,7 @@ static int msm_isp_cfg_rdi(struct vfe_device *vfe_dev, return rc; } -int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg) +static int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg) { int rc = 0; struct msm_vfe_input_cfg *input_cfg = arg; @@ -542,7 +542,7 @@ int msm_isp_cfg_input(struct vfe_device *vfe_dev, void *arg) return rc; } -int msm_isp_camif_cfg(struct vfe_device *vfe_dev, void *arg) +static int msm_isp_camif_cfg(struct vfe_device *vfe_dev, void *arg) { int rc = 0; struct msm_vfe_camif_cfg *camif_cfg = arg; @@ -579,7 +579,7 @@ int msm_isp_camif_cfg(struct vfe_device *vfe_dev, void *arg) } -int msm_isp_operation_cfg(struct vfe_device *vfe_dev, void *arg) +static int msm_isp_operation_cfg(struct vfe_device *vfe_dev, void *arg) { struct msm_vfe_operation_cfg *op_cfg = arg; @@ -1233,14 +1233,16 @@ static int msm_isp_send_hw_cmd(struct vfe_device *vfe_dev, case VFE_WRITE: { msm_camera_io_memcpy(vfe_dev->vfe_base + reg_cfg_cmd->u.rw_info.reg_offset, - cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4, + (void __iomem *) + (cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4), reg_cfg_cmd->u.rw_info.len); break; } case VFE_WRITE_MB: { msm_camera_io_memcpy_mb(vfe_dev->vfe_base + reg_cfg_cmd->u.rw_info.reg_offset, - cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4, + (void __iomem *) + (cfg_data + reg_cfg_cmd->u.rw_info.cmd_data_offset/4), reg_cfg_cmd->u.rw_info.len); break; } @@ -2295,12 +2297,12 @@ int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) } #ifdef CONFIG_MSM_AVTIMER -void msm_isp_end_avtimer(void) +static void msm_isp_end_avtimer(void) { avcs_core_disable_power_collapse(0); } #else -void msm_isp_end_avtimer(void) +static void msm_isp_end_avtimer(void) { pr_err("AV Timer is not supported\n"); } @@ -2408,7 +2410,7 @@ void msm_isp_save_framedrop_values(struct vfe_device *vfe_dev, } } -void msm_isp_dump_irq_debug(void) +static void msm_isp_dump_irq_debug(void) { uint32_t index, count, i; diff --git a/drivers/media/platform/msm/ais/msm.c b/drivers/media/platform/msm/ais/msm.c index e8859b7db5cb..3c2e28eaa4f9 100644 --- a/drivers/media/platform/msm/ais/msm.c +++ b/drivers/media/platform/msm/ais/msm.c @@ -48,10 +48,10 @@ bool is_daemon_status = true; /* config node envent queue */ static struct v4l2_fh *msm_eventq; -spinlock_t msm_eventq_lock; +static spinlock_t msm_eventq_lock; static struct pid *msm_pid; -spinlock_t msm_pid_lock; +static spinlock_t msm_pid_lock; /* * It takes 20 bytes + NULL character to write the @@ -62,7 +62,7 @@ spinlock_t msm_pid_lock; #define msm_dequeue(queue, type, member) ({ \ unsigned long flags; \ struct msm_queue_head *__q = (queue); \ - type *node = 0; \ + type *node = NULL; \ spin_lock_irqsave(&__q->lock, flags); \ if (!list_empty(&__q->list)) { \ __q->len--; \ @@ -78,7 +78,7 @@ spinlock_t msm_pid_lock; #define msm_delete_sd_entry(queue, type, member, q_node) ({ \ unsigned long flags; \ struct msm_queue_head *__q = (queue); \ - type *node = 0; \ + type *node = NULL; \ spin_lock_irqsave(&__q->lock, flags); \ if (!list_empty(&__q->list)) { \ list_for_each_entry(node, &__q->list, member) \ @@ -95,7 +95,7 @@ spinlock_t msm_pid_lock; #define msm_delete_entry(queue, type, member, q_node) ({ \ unsigned long flags; \ struct msm_queue_head *__q = (queue); \ - type *node = 0; \ + type *node = NULL; \ spin_lock_irqsave(&__q->lock, flags); \ if (!list_empty(&__q->list)) { \ list_for_each_entry(node, &__q->list, member) \ @@ -131,7 +131,7 @@ typedef int (*msm_queue_func)(void *d1, void *d2); #define msm_queue_traverse_action(queue, type, member, func, data) do {\ unsigned long flags; \ struct msm_queue_head *__q = (queue); \ - type *node = 0; \ + type *node = NULL; \ msm_queue_func __f = (func); \ spin_lock_irqsave(&__q->lock, flags); \ if (!list_empty(&__q->list)) { \ @@ -147,7 +147,7 @@ typedef int (*msm_queue_find_func)(void *d1, void *d2); #define msm_queue_find(queue, type, member, func, data) ({\ unsigned long flags; \ struct msm_queue_head *__q = (queue); \ - type *node = 0; \ + type *node = NULL; \ typeof(node) __ret = NULL; \ msm_queue_find_func __f = (func); \ spin_lock_irqsave(&__q->lock, flags); \ @@ -283,22 +283,47 @@ void msm_delete_stream(unsigned int session_id, unsigned int stream_id) struct msm_session *session = NULL; struct msm_stream *stream = NULL; unsigned long flags; + int try_count = 0; session = msm_queue_find(msm_session_q, struct msm_session, list, __msm_queue_find_session, &session_id); + if (!session) return; - stream = msm_queue_find(&session->stream_q, struct msm_stream, - list, __msm_queue_find_stream, &stream_id); - if (!stream) - return; - spin_lock_irqsave(&(session->stream_q.lock), flags); - list_del_init(&stream->list); - session->stream_q.len--; - kfree(stream); - stream = NULL; - spin_unlock_irqrestore(&(session->stream_q.lock), flags); + while (1) { + + if (try_count > 5) { + pr_err("%s : not able to delete stream %d\n", + __func__, __LINE__); + break; + } + + write_lock(&session->stream_rwlock); + try_count++; + stream = msm_queue_find(&session->stream_q, struct msm_stream, + list, __msm_queue_find_stream, &stream_id); + + if (!stream) { + write_unlock(&session->stream_rwlock); + return; + } + + if (msm_vb2_get_stream_state(stream) != 1) { + write_unlock(&session->stream_rwlock); + continue; + } + + spin_lock_irqsave(&(session->stream_q.lock), flags); + list_del_init(&stream->list); + session->stream_q.len--; + kfree(stream); + stream = NULL; + spin_unlock_irqrestore(&(session->stream_q.lock), flags); + write_unlock(&session->stream_rwlock); + break; + } + } EXPORT_SYMBOL(msm_delete_stream); @@ -446,6 +471,7 @@ int msm_create_session(unsigned int session_id, struct video_device *vdev) mutex_init(&session->lock); mutex_init(&session->lock_q); mutex_init(&session->close_lock); + rwlock_init(&session->stream_rwlock); return 0; } EXPORT_SYMBOL(msm_create_session); @@ -1040,17 +1066,25 @@ static struct v4l2_file_operations msm_fops = { #endif }; -struct msm_stream *msm_get_stream(unsigned int session_id, - unsigned int stream_id) +struct msm_session *msm_get_session(unsigned int session_id) { struct msm_session *session; - struct msm_stream *stream; session = msm_queue_find(msm_session_q, struct msm_session, list, __msm_queue_find_session, &session_id); if (!session) return ERR_PTR(-EINVAL); + return session; +} +EXPORT_SYMBOL(msm_get_session); + + +struct msm_stream *msm_get_stream(struct msm_session *session, + unsigned int stream_id) +{ + struct msm_stream *stream; + stream = msm_queue_find(&session->stream_q, struct msm_stream, list, __msm_queue_find_stream, &stream_id); @@ -1108,6 +1142,34 @@ struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q) } EXPORT_SYMBOL(msm_get_stream_from_vb2q); +struct msm_session *msm_get_session_from_vb2q(struct vb2_queue *q) +{ + struct msm_session *session; + struct msm_stream *stream; + unsigned long flags1; + unsigned long flags2; + + spin_lock_irqsave(&msm_session_q->lock, flags1); + list_for_each_entry(session, &(msm_session_q->list), list) { + spin_lock_irqsave(&(session->stream_q.lock), flags2); + list_for_each_entry( + stream, &(session->stream_q.list), list) { + if (stream->vb2_q == q) { + spin_unlock_irqrestore + (&(session->stream_q.lock), flags2); + spin_unlock_irqrestore + (&msm_session_q->lock, flags1); + return session; + } + } + spin_unlock_irqrestore(&(session->stream_q.lock), flags2); + } + spin_unlock_irqrestore(&msm_session_q->lock, flags1); + return NULL; +} +EXPORT_SYMBOL(msm_get_session_from_vb2q); + + #ifdef CONFIG_COMPAT long msm_copy_camera_private_ioctl_args(unsigned long arg, struct msm_camera_private_ioctl_arg *k_ioctl, @@ -1119,7 +1181,7 @@ long msm_copy_camera_private_ioctl_args(unsigned long arg, return -EIO; if (copy_from_user(&up_ioctl, - (struct msm_camera_private_ioctl_arg *)arg, + (void __user *)arg, sizeof(struct msm_camera_private_ioctl_arg))) return -EFAULT; diff --git a/drivers/media/platform/msm/ais/msm.h b/drivers/media/platform/msm/ais/msm.h index d8b2d5871fc2..5d456310c301 100644 --- a/drivers/media/platform/msm/ais/msm.h +++ b/drivers/media/platform/msm/ais/msm.h @@ -114,6 +114,7 @@ struct msm_session { struct mutex lock; struct mutex lock_q; struct mutex close_lock; + rwlock_t stream_rwlock; }; static inline bool msm_is_daemon_present(void) @@ -131,11 +132,13 @@ int msm_create_stream(unsigned int session_id, void msm_delete_stream(unsigned int session_id, unsigned int stream_id); int msm_create_command_ack_q(unsigned int session_id, unsigned int stream_id); void msm_delete_command_ack_q(unsigned int session_id, unsigned int stream_id); -struct msm_stream *msm_get_stream(unsigned int session_id, +struct msm_session *msm_get_session(unsigned int session_id); +struct msm_stream *msm_get_stream(struct msm_session *session, unsigned int stream_id); struct vb2_queue *msm_get_stream_vb2q(unsigned int session_id, unsigned int stream_id); struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q); +struct msm_session *msm_get_session_from_vb2q(struct vb2_queue *q); struct msm_session *msm_session_find(unsigned int session_id); #ifdef CONFIG_COMPAT long msm_copy_camera_private_ioctl_args(unsigned long arg, diff --git a/drivers/media/platform/msm/ais/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/ais/msm_buf_mgr/msm_generic_buf_mgr.c index 073b91a6d2d9..66751b1f0657 100644 --- a/drivers/media/platform/msm/ais/msm_buf_mgr/msm_generic_buf_mgr.c +++ b/drivers/media/platform/msm/ais/msm_buf_mgr/msm_generic_buf_mgr.c @@ -51,7 +51,7 @@ static int32_t msm_buf_mngr_hdl_cont_get_buf(struct msm_buf_mngr_device *dev, } static int32_t msm_buf_mngr_get_buf(struct msm_buf_mngr_device *dev, - void __user *argp) + void *argp) { unsigned long flags; int32_t rc = 0; @@ -465,7 +465,7 @@ static int msm_generic_buf_mngr_close(struct v4l2_subdev *sd, return rc; } -int msm_cam_buf_mgr_ops(unsigned int cmd, void *argp) +static int msm_cam_buf_mgr_ops(unsigned int cmd, void *argp) { int rc = 0; @@ -531,7 +531,7 @@ static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd, { int32_t rc = 0; struct msm_buf_mngr_device *buf_mngr_dev = v4l2_get_subdevdata(sd); - void __user *argp = (void __user *)arg; + void *argp = arg; if (!buf_mngr_dev) { pr_err("%s buf manager device NULL\n", __func__); @@ -557,13 +557,13 @@ static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd, MSM_CAM_GET_IOCTL_ARG_PTR(&tmp, &k_ioctl.ioctl_ptr, sizeof(tmp)); - if (copy_from_user(&buf_info, tmp, + if (copy_from_user(&buf_info, (void __user *)tmp, sizeof(struct msm_buf_mngr_info))) { return -EFAULT; } k_ioctl.ioctl_ptr = (uintptr_t)&buf_info; - argp = &k_ioctl; + argp = (void *)&k_ioctl; rc = msm_cam_buf_mgr_ops(cmd, argp); } break; diff --git a/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c index 280bf4ebb596..36aa3f62fbec 100644 --- a/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c +++ b/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.c @@ -41,20 +41,28 @@ static int msm_vb2_queue_setup(struct vb2_queue *q, return 0; } -int msm_vb2_buf_init(struct vb2_buffer *vb) +static int msm_vb2_buf_init(struct vb2_buffer *vb) { struct msm_stream *stream; + struct msm_session *session; struct msm_vb2_buffer *msm_vb2_buf; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + session = msm_get_session_from_vb2q(vb->vb2_queue); + if (IS_ERR_OR_NULL(session)) + return -EINVAL; + + read_lock(&session->stream_rwlock); + stream = msm_get_stream_from_vb2q(vb->vb2_queue); if (!stream) { pr_err("%s: Couldn't find stream\n", __func__); + read_unlock(&session->stream_rwlock); return -EINVAL; } msm_vb2_buf = container_of(vbuf, struct msm_vb2_buffer, vb2_v4l2_buf); msm_vb2_buf->in_freeq = 0; - + read_unlock(&session->stream_rwlock); return 0; } @@ -62,6 +70,7 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb) { struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; + struct msm_session *session; unsigned long flags; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); @@ -71,21 +80,30 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb) return; } + session = msm_get_session_from_vb2q(vb->vb2_queue); + if (IS_ERR_OR_NULL(session)) + return; + + read_lock(&session->stream_rwlock); + stream = msm_get_stream_from_vb2q(vb->vb2_queue); if (!stream) { pr_err("%s:%d] NULL stream", __func__, __LINE__); + read_unlock(&session->stream_rwlock); return; } spin_lock_irqsave(&stream->stream_lock, flags); list_add_tail(&msm_vb2->list, &stream->queued_list); spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); } static void msm_vb2_buf_finish(struct vb2_buffer *vb) { struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; + struct msm_session *session; unsigned long flags; struct msm_vb2_buffer *msm_vb2_entry, *temp; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); @@ -96,9 +114,16 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb) return; } + session = msm_get_session_from_vb2q(vb->vb2_queue); + if (IS_ERR_OR_NULL(session)) + return; + + read_lock(&session->stream_rwlock); + stream = msm_get_stream_from_vb2q(vb->vb2_queue); if (!stream) { pr_err("%s:%d] NULL stream", __func__, __LINE__); + read_unlock(&session->stream_rwlock); return; } @@ -111,18 +136,27 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb) } } spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); } static void msm_vb2_stop_stream(struct vb2_queue *q) { struct msm_vb2_buffer *msm_vb2, *temp; struct msm_stream *stream; + struct msm_session *session; unsigned long flags; struct vb2_v4l2_buffer *vb2_v4l2_buf; + session = msm_get_session_from_vb2q(q); + if (IS_ERR_OR_NULL(session)) + return; + + read_lock(&session->stream_rwlock); + stream = msm_get_stream_from_vb2q(q); if (!stream) { pr_err_ratelimited("%s:%d] NULL stream", __func__, __LINE__); + read_unlock(&session->stream_rwlock); return; } @@ -142,8 +176,28 @@ static void msm_vb2_stop_stream(struct vb2_queue *q) msm_vb2->in_freeq = 0; } spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); } +int msm_vb2_get_stream_state(struct msm_stream *stream) +{ + struct msm_vb2_buffer *msm_vb2, *temp; + unsigned long flags; + int rc = 1; + + spin_lock_irqsave(&stream->stream_lock, flags); + list_for_each_entry_safe(msm_vb2, temp, &(stream->queued_list), list) { + if (msm_vb2->in_freeq != 0) { + rc = 0; + break; + } + } + spin_unlock_irqrestore(&stream->stream_lock, flags); + return rc; +} +EXPORT_SYMBOL(msm_vb2_get_stream_state); + + static struct vb2_ops msm_vb2_get_q_op = { .queue_setup = msm_vb2_queue_setup, .buf_init = msm_vb2_buf_init, @@ -199,13 +253,22 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id, { struct msm_stream *stream; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; + struct msm_session *session; struct msm_vb2_buffer *msm_vb2 = NULL; unsigned long flags; - stream = msm_get_stream(session_id, stream_id); - if (IS_ERR_OR_NULL(stream)) + session = msm_get_session(session_id); + if (IS_ERR_OR_NULL(session)) return NULL; + read_lock(&session->stream_rwlock); + + stream = msm_get_stream(session, stream_id); + if (IS_ERR_OR_NULL(stream)) { + read_unlock(&session->stream_rwlock); + return NULL; + } + spin_lock_irqsave(&stream->stream_lock, flags); if (!stream->vb2_q) { @@ -228,6 +291,7 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id, vb2_v4l2_buf = NULL; end: spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return vb2_v4l2_buf; } @@ -236,12 +300,22 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id, { struct msm_stream *stream; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; + struct msm_session *session; struct msm_vb2_buffer *msm_vb2 = NULL; unsigned long flags; - stream = msm_get_stream(session_id, stream_id); - if (IS_ERR_OR_NULL(stream)) + session = msm_get_session(session_id); + if (IS_ERR_OR_NULL(session)) + return NULL; + + read_lock(&session->stream_rwlock); + + stream = msm_get_stream(session, stream_id); + + if (IS_ERR_OR_NULL(stream)) { + read_unlock(&session->stream_rwlock); return NULL; + } spin_lock_irqsave(&stream->stream_lock, flags); @@ -263,6 +337,7 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id, vb2_v4l2_buf = NULL; end: spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return vb2_v4l2_buf; } @@ -270,15 +345,24 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id, unsigned int stream_id) { struct msm_stream *stream; + struct msm_session *session; struct msm_vb2_buffer *msm_vb2; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; int rc = 0; unsigned long flags; - stream = msm_get_stream(session_id, stream_id); - if (IS_ERR_OR_NULL(stream)) + session = msm_get_session(session_id); + if (IS_ERR_OR_NULL(session)) return -EINVAL; + read_lock(&session->stream_rwlock); + + stream = msm_get_stream(session, stream_id); + if (IS_ERR_OR_NULL(stream)) { + read_unlock(&session->stream_rwlock); + return -EINVAL; + } + spin_lock_irqsave(&stream->stream_lock, flags); if (vb) { list_for_each_entry(msm_vb2, &(stream->queued_list), list) { @@ -306,6 +390,7 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id, rc = -EINVAL; } spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return rc; } @@ -317,11 +402,21 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id, struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; + struct msm_session *session; int rc = 0; - stream = msm_get_stream(session_id, stream_id); - if (IS_ERR_OR_NULL(stream)) + session = msm_get_session(session_id); + if (IS_ERR_OR_NULL(session)) return -EINVAL; + + read_lock(&session->stream_rwlock); + + stream = msm_get_stream(session, stream_id); + if (IS_ERR_OR_NULL(stream)) { + read_unlock(&session->stream_rwlock); + return -EINVAL; + } + spin_lock_irqsave(&stream->stream_lock, flags); if (vb) { list_for_each_entry(msm_vb2, &(stream->queued_list), list) { @@ -353,6 +448,7 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id, rc = -EINVAL; } spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return rc; } @@ -361,14 +457,23 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id, { struct msm_stream *stream; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; + struct msm_session *session; struct msm_vb2_buffer *msm_vb2 = NULL; unsigned long flags; long rc = -EINVAL; - stream = msm_get_stream(session_id, stream_id); - if (IS_ERR_OR_NULL(stream)) + session = msm_get_session(session_id); + if (IS_ERR_OR_NULL(session)) return rc; + read_lock(&session->stream_rwlock); + + stream = msm_get_stream(session, stream_id); + if (IS_ERR_OR_NULL(stream)) { + read_unlock(&session->stream_rwlock); + return -EINVAL; + } + spin_lock_irqsave(&stream->stream_lock, flags); if (!stream->vb2_q) { @@ -394,6 +499,7 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id, end: spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return rc; } EXPORT_SYMBOL(msm_vb2_return_buf_by_idx); @@ -404,10 +510,20 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id) struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; + struct msm_session *session; + + session = msm_get_session(session_id); + if (IS_ERR_OR_NULL(session)) + return -EINVAL; - stream = msm_get_stream(session_id, stream_id); - if (IS_ERR_OR_NULL(stream)) + read_lock(&session->stream_rwlock); + + stream = msm_get_stream(session, stream_id); + if (IS_ERR_OR_NULL(stream)) { + read_unlock(&session->stream_rwlock); return -EINVAL; + } + spin_lock_irqsave(&stream->stream_lock, flags); list_for_each_entry(msm_vb2, &(stream->queued_list), list) { vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf); @@ -416,6 +532,7 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id) msm_vb2->in_freeq = 0; } spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return 0; } diff --git a/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.h b/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.h index 3dbb21332857..0f57112e82f2 100644 --- a/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.h +++ b/drivers/media/platform/msm/ais/msm_vb2/msm_vb2.h @@ -68,5 +68,6 @@ struct vb2_mem_ops *msm_vb2_get_q_mem_ops(void); int msm_vb2_request_cb(struct msm_sd_req_vb2_q *req_sd); long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id, uint32_t index); +int msm_vb2_get_stream_state(struct msm_stream *stream); #endif /*_MSM_VB_H */ diff --git a/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c index 8df56fe526fe..1adb380f335f 100644 --- a/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c @@ -522,7 +522,7 @@ static int32_t msm_actuator_piezo_move_focus( CDBG("Enter\n"); if (copy_from_user(&ringing_params_kernel, - &(move_params->ringing_params[0]), + (void __user *)&(move_params->ringing_params[0]), sizeof(struct damping_params_t))) { pr_err("copy_from_user failed\n"); return -EFAULT; @@ -612,7 +612,7 @@ static int32_t msm_actuator_move_focus( return -EFAULT; } if (copy_from_user(ringing_params_kernel, - &(move_params->ringing_params[0]), + (void __user *)&(move_params->ringing_params[0]), (sizeof(struct damping_params_t))*(a_ctrl->region_size))) { pr_err("copy_from_user failed\n"); /* Free the allocated memory for damping parameters */ @@ -732,7 +732,7 @@ static int32_t msm_actuator_bivcm_move_focus( return -EFAULT; } if (copy_from_user(ringing_params_kernel, - &(move_params->ringing_params[0]), + (void __user *)&(move_params->ringing_params[0]), (sizeof(struct damping_params_t))*(a_ctrl->region_size))) { pr_err("copy_from_user failed\n"); /* Free the allocated memory for damping parameters */ @@ -1289,7 +1289,7 @@ static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl, a_ctrl->total_steps = set_info->af_tuning_params.total_steps; if (copy_from_user(&a_ctrl->region_params, - (void *)set_info->af_tuning_params.region_params, + (void __user *)set_info->af_tuning_params.region_params, a_ctrl->region_size * sizeof(struct region_params_t))) return -EFAULT; @@ -1332,7 +1332,7 @@ static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl, } if (copy_from_user(&a_ctrl->reg_tbl, - (void *)set_info->actuator_params.reg_tbl_params, + (void __user *)set_info->actuator_params.reg_tbl_params, a_ctrl->reg_tbl_size * sizeof(struct msm_actuator_reg_params_t))) { kfree(a_ctrl->i2c_reg_tbl); @@ -1354,7 +1354,8 @@ static int32_t msm_actuator_set_param(struct msm_actuator_ctrl_t *a_ctrl, return -EFAULT; } if (copy_from_user(init_settings, - (void *)set_info->actuator_params.init_settings, + (void __user *) + set_info->actuator_params.init_settings, set_info->actuator_params.init_setting_size * sizeof(struct reg_settings_t))) { kfree(init_settings); @@ -1411,7 +1412,7 @@ static int msm_actuator_init(struct msm_actuator_ctrl_t *a_ctrl) } static int32_t msm_actuator_config(struct msm_actuator_ctrl_t *a_ctrl, - void __user *argp) + void *argp) { struct msm_actuator_cfg_data *cdata = (struct msm_actuator_cfg_data *)argp; @@ -1571,7 +1572,7 @@ static long msm_actuator_subdev_ioctl(struct v4l2_subdev *sd, { int rc; struct msm_actuator_ctrl_t *a_ctrl = v4l2_get_subdevdata(sd); - void __user *argp = (void __user *)arg; + void *argp = arg; CDBG("Enter\n"); CDBG("%s:%d a_ctrl %pK argp %pK\n", __func__, __LINE__, a_ctrl, argp); diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_0_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_0_hwreg.h index f88c0ef82499..f55e6c344ef1 100644 --- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_0_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_0_hwreg.h @@ -15,9 +15,9 @@ #include <sensor/csid/msm_csid.h> -uint8_t csid_lane_assign_v2_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; +static uint8_t csid_lane_assign_v2_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; -struct csid_reg_parms_t csid_v2_0 = { +static struct csid_reg_parms_t csid_v2_0 = { /* MIPI CSID registers */ 0x0, diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_2_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_2_hwreg.h index e2bb6cd499ff..9ba3555ff01f 100644 --- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_2_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_2_2_hwreg.h @@ -15,9 +15,9 @@ #include <sensor/csid/msm_csid.h> -uint8_t csid_lane_assign_v2_2[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; +static uint8_t csid_lane_assign_v2_2[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; -struct csid_reg_parms_t csid_v2_2 = { +static struct csid_reg_parms_t csid_v2_2 = { /* MIPI CSID registers */ 0x0, 0x4, diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_0_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_0_hwreg.h index 440f869692f7..c75c4167453c 100644 --- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_0_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_0_hwreg.h @@ -15,9 +15,9 @@ #include <sensor/csid/msm_csid.h> -uint8_t csid_lane_assign_v3_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; +static uint8_t csid_lane_assign_v3_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; -struct csid_reg_parms_t csid_v3_0 = { +static struct csid_reg_parms_t csid_v3_0 = { /* MIPI CSID registers */ 0x0, 0x4, diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_1_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_1_hwreg.h index dde47046b679..dc71f39a38f1 100644 --- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_1_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_1_hwreg.h @@ -15,9 +15,9 @@ #include <sensor/csid/msm_csid.h> -uint8_t csid_lane_assign_v3_1[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; +static uint8_t csid_lane_assign_v3_1[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; -struct csid_reg_parms_t csid_v3_1 = { +static struct csid_reg_parms_t csid_v3_1 = { /* MIPI CSID registers */ 0x0, 0x4, diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_2_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_2_hwreg.h index 5241a90fbc86..00085dbf94a0 100644 --- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_2_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_2_hwreg.h @@ -15,9 +15,9 @@ #include <sensor/csid/msm_csid.h> -uint8_t csid_lane_assign_v3_2[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; +static uint8_t csid_lane_assign_v3_2[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; -struct csid_reg_parms_t csid_v3_2 = { +static struct csid_reg_parms_t csid_v3_2 = { /* MIPI CSID registers */ 0x0, 0x4, diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_1_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_1_hwreg.h index 0e8ff6c0986d..1d465b66b33f 100644 --- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_1_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_1_hwreg.h @@ -14,9 +14,9 @@ #define MSM_CSID_3_4_1_HWREG_H #include <sensor/csid/msm_csid.h> -uint8_t csid_lane_assign_v3_4_1[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; +static uint8_t csid_lane_assign_v3_4_1[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; -struct csid_reg_parms_t csid_v3_4_1 = { +static struct csid_reg_parms_t csid_v3_4_1 = { /* MIPI CSID registers */ 0x0, 0x4, diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_2_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_2_hwreg.h index 651526cb3db8..d78e68e090e7 100644 --- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_2_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_2_hwreg.h @@ -15,8 +15,8 @@ #include <sensor/csid/msm_csid.h> -uint8_t csid_lane_assign_v3_4_2[PHY_LANE_MAX] = {0, 4, 1, 2, 3}; -struct csid_reg_parms_t csid_v3_4_2 = { +static uint8_t csid_lane_assign_v3_4_2[PHY_LANE_MAX] = {0, 4, 1, 2, 3}; +static struct csid_reg_parms_t csid_v3_4_2 = { /* MIPI CSID registers */ 0x0, 0x4, diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_3_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_3_hwreg.h index fff29fc9d4c4..bbf4b287ffe4 100644 --- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_3_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_4_3_hwreg.h @@ -15,8 +15,8 @@ #include <sensor/csid/msm_csid.h> -uint8_t csid_lane_assign_v3_4_3[PHY_LANE_MAX] = {0, 4, 1, 2, 3}; -struct csid_reg_parms_t csid_v3_4_3 = { +static uint8_t csid_lane_assign_v3_4_3[PHY_LANE_MAX] = {0, 4, 1, 2, 3}; +static struct csid_reg_parms_t csid_v3_4_3 = { /* MIPI CSID registers */ 0x0, 0x4, diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_1_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_1_hwreg.h index f7d7d3548c4b..534ef3f5533c 100644 --- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_1_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_1_hwreg.h @@ -15,9 +15,9 @@ #include <sensor/csid/msm_csid.h> -uint8_t csid_lane_assign_v3_5_1[PHY_LANE_MAX] = {0, 4, 1, 2, 3}; +static uint8_t csid_lane_assign_v3_5_1[PHY_LANE_MAX] = {0, 4, 1, 2, 3}; -struct csid_reg_parms_t csid_v3_5_1 = { +static struct csid_reg_parms_t csid_v3_5_1 = { /* MIPI CSID registers */ 0x0, 0x4, diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_hwreg.h index b423b6e510a0..392d902d3e0c 100644 --- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_5_hwreg.h @@ -15,9 +15,9 @@ #include <sensor/csid/msm_csid.h> -uint8_t csid_lane_assign_v3_5[PHY_LANE_MAX] = {0, 4, 1, 2, 3}; +static uint8_t csid_lane_assign_v3_5[PHY_LANE_MAX] = {0, 4, 1, 2, 3}; -struct csid_reg_parms_t csid_v3_5 = { +static struct csid_reg_parms_t csid_v3_5 = { /* MIPI CSID registers */ 0x0, 0x4, diff --git a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_6_0_hwreg.h b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_6_0_hwreg.h index b95a774ca737..6722974f889b 100644 --- a/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_6_0_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csid/include/msm_csid_3_6_0_hwreg.h @@ -15,8 +15,8 @@ #include <sensor/csid/msm_csid.h> -uint8_t csid_lane_assign_v3_6_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; -struct csid_reg_parms_t csid_v3_6_0 = { +static uint8_t csid_lane_assign_v3_6_0[PHY_LANE_MAX] = {0, 1, 2, 3, 4}; +static struct csid_reg_parms_t csid_v3_6_0 = { /* MIPI CSID registers */ 0x0, 0x4, diff --git a/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c b/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c index 331ba939adfa..2b3eefa65606 100644 --- a/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c +++ b/drivers/media/platform/msm/ais/sensor/csid/msm_csid.c @@ -708,7 +708,7 @@ static int msm_csid_release(struct csid_device *csid_dev) return 0; } -static int32_t msm_csid_cmd(struct csid_device *csid_dev, void __user *arg) +static int32_t msm_csid_cmd(struct csid_device *csid_dev, void *arg) { int rc = 0; struct csid_cfg_data *cdata = (struct csid_cfg_data *)arg; @@ -728,7 +728,7 @@ static int32_t msm_csid_cmd(struct csid_device *csid_dev, void __user *arg) case CSID_TESTMODE_CFG: { csid_dev->is_testmode = 1; if (copy_from_user(&csid_dev->testmode_params, - (void *)cdata->cfg.csid_testmode_params, + (void __user *)cdata->cfg.csid_testmode_params, sizeof(struct msm_camera_csid_testmode_parms))) { pr_err("%s: %d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -741,7 +741,7 @@ static int32_t msm_csid_cmd(struct csid_device *csid_dev, void __user *arg) int i = 0; if (copy_from_user(&csid_params, - (void *)cdata->cfg.csid_params, + (void __user *)cdata->cfg.csid_params, sizeof(struct msm_camera_csid_params))) { pr_err("%s: %d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -790,7 +790,7 @@ static int32_t msm_csid_cmd(struct csid_device *csid_dev, void __user *arg) int i = 0; if (copy_from_user(&csid_params, - (void *)cdata->cfg.csid_params, + (void __user *)cdata->cfg.csid_params, sizeof(struct msm_camera_csid_params))) { pr_err("%s: %d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -892,7 +892,7 @@ static long msm_csid_subdev_ioctl(struct v4l2_subdev *sd, #ifdef CONFIG_COMPAT -static int32_t msm_csid_cmd32(struct csid_device *csid_dev, void __user *arg) +static int32_t msm_csid_cmd32(struct csid_device *csid_dev, void *arg) { int rc = 0; struct csid_cfg_data32 *arg32 = (struct csid_cfg_data32 *) (arg); @@ -913,7 +913,8 @@ static int32_t msm_csid_cmd32(struct csid_device *csid_dev, void __user *arg) case CSID_TESTMODE_CFG: { csid_dev->is_testmode = 1; if (copy_from_user(&csid_dev->testmode_params, - (void *)compat_ptr(arg32->cfg.csid_testmode_params), + (void __user *) + compat_ptr(arg32->cfg.csid_testmode_params), sizeof(struct msm_camera_csid_testmode_parms))) { pr_err("%s: %d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -926,7 +927,7 @@ static int32_t msm_csid_cmd32(struct csid_device *csid_dev, void __user *arg) struct msm_camera_csid_params32 csid_params32; if (copy_from_user(&csid_params32, - (void *)compat_ptr(arg32->cfg.csid_params), + (void __user *)compat_ptr(arg32->cfg.csid_params), sizeof(struct msm_camera_csid_params32))) { pr_err("%s: %d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -975,7 +976,7 @@ static int32_t msm_csid_cmd32(struct csid_device *csid_dev, void __user *arg) struct msm_camera_csid_params32 csid_params32; if (copy_from_user(&csid_params32, - (void *)compat_ptr(arg32->cfg.csid_params), + (void __user *)compat_ptr(arg32->cfg.csid_params), sizeof(struct msm_camera_csid_params32))) { pr_err("%s: %d failed\n", __func__, __LINE__); rc = -EFAULT; diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h index 618926fa8341..3b377de66a2c 100644 --- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_0_hwreg.h @@ -15,7 +15,7 @@ #include <sensor/csiphy/msm_csiphy.h> -struct csiphy_reg_parms_t csiphy_v2_0 = { +static struct csiphy_reg_parms_t csiphy_v2_0 = { /* MIPI CSI PHY registers */ 0x17C, 0x0, diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h index 867aec2e0103..71b07299c342 100644 --- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_2_2_hwreg.h @@ -15,7 +15,7 @@ #include <sensor/csiphy/msm_csiphy.h> -struct csiphy_reg_parms_t csiphy_v2_2 = { +static struct csiphy_reg_parms_t csiphy_v2_2 = { /* MIPI CSI PHY registers */ 0x17C, 0x0, diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h index 69efdcc71499..8846fde0f6ed 100644 --- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_0_hwreg.h @@ -15,7 +15,7 @@ #include <sensor/csiphy/msm_csiphy.h> -struct csiphy_reg_parms_t csiphy_v3_0 = { +static struct csiphy_reg_parms_t csiphy_v3_0 = { /* MIPI CSI PHY registers */ 0x0, 0x4, diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h index 7fc74a366a6c..044e16ef3848 100644 --- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_1_hwreg.h @@ -15,7 +15,7 @@ #include <sensor/csiphy/msm_csiphy.h> -struct csiphy_reg_parms_t csiphy_v3_1 = { +static struct csiphy_reg_parms_t csiphy_v3_1 = { /* MIPI CSI PHY registers */ 0x0, 0x4, diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h index cdf62d46ee7d..c01f0540dfd2 100644 --- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_2_hwreg.h @@ -15,7 +15,7 @@ #include <sensor/csiphy/msm_csiphy.h> -struct csiphy_reg_parms_t csiphy_v3_2 = { +static struct csiphy_reg_parms_t csiphy_v3_2 = { /* MIPI CSI PHY registers */ 0x0, 0x4, diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_1_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_1_hwreg.h index 5af1ded189a6..78ac19993fee 100644 --- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_1_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_1_hwreg.h @@ -18,14 +18,14 @@ #include <sensor/csiphy/msm_csiphy.h> -struct csiphy_reg_parms_t csiphy_v3_4_2_1 = { +static struct csiphy_reg_parms_t csiphy_v3_4_2_1 = { .mipi_csiphy_interrupt_status0_addr = 0x8B0, .mipi_csiphy_interrupt_clear0_addr = 0x858, .mipi_csiphy_glbl_irq_cmd_addr = 0x828, .combo_clk_mask = 0x10, }; -struct csiphy_reg_3ph_parms_t csiphy_v3_4_2_1_3ph = { +static struct csiphy_reg_3ph_parms_t csiphy_v3_4_2_1_3ph = { /* MIPI CSI PHY registers */ {0x814, 0x0}, {0x818, 0x1}, diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h index d85dd1ec3a48..e6072e747a63 100644 --- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_4_2_hwreg.h @@ -18,14 +18,14 @@ #include <sensor/csiphy/msm_csiphy.h> -struct csiphy_reg_parms_t csiphy_v3_4_2 = { +static struct csiphy_reg_parms_t csiphy_v3_4_2 = { .mipi_csiphy_interrupt_status0_addr = 0x8B0, .mipi_csiphy_interrupt_clear0_addr = 0x858, .mipi_csiphy_glbl_irq_cmd_addr = 0x828, .combo_clk_mask = 0x10, }; -struct csiphy_reg_3ph_parms_t csiphy_v3_4_2_3ph = { +static struct csiphy_reg_3ph_parms_t csiphy_v3_4_2_3ph = { /* MIPI CSI PHY registers */ {0x814, 0x0}, {0x818, 0x1}, diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h index 99b725a75c8f..bc70697cce5c 100644 --- a/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h +++ b/drivers/media/platform/msm/ais/sensor/csiphy/include/msm_csiphy_3_5_hwreg.h @@ -18,14 +18,14 @@ #include <sensor/csiphy/msm_csiphy.h> -struct csiphy_reg_parms_t csiphy_v3_5 = { +static struct csiphy_reg_parms_t csiphy_v3_5 = { .mipi_csiphy_interrupt_status0_addr = 0x8B0, .mipi_csiphy_interrupt_clear0_addr = 0x858, .mipi_csiphy_glbl_irq_cmd_addr = 0x828, .combo_clk_mask = 0x10, }; -struct csiphy_reg_3ph_parms_t csiphy_v3_5_3ph = { +static struct csiphy_reg_3ph_parms_t csiphy_v3_5_3ph = { /* MIPI CSI PHY registers */ {0x814, 0x0}, {0x818, 0x1}, diff --git a/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c index d146cc3d28a5..c3b087f61888 100644 --- a/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c +++ b/drivers/media/platform/msm/ais/sensor/csiphy/msm_csiphy.c @@ -164,7 +164,7 @@ static int msm_csiphy_3phase_lane_config( mipi_csiphy_3ph_lnn_ctrl1.data, csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. mipi_csiphy_3ph_lnn_ctrl1.addr + 0x200*i); - msm_camera_io_w(((csiphy_params->settle_cnt >> 8) & 0xff), + msm_camera_io_w(0, csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. mipi_csiphy_3ph_lnn_ctrl2.addr + 0x200*i); msm_camera_io_w((csiphy_params->settle_cnt & 0xff), @@ -648,7 +648,7 @@ static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev, return rc; } -void msm_csiphy_disable_irq( +static void msm_csiphy_disable_irq( struct csiphy_device *csiphy_dev) { void __iomem *csiphybase; @@ -1207,7 +1207,7 @@ static int32_t msm_csiphy_cmd(struct csiphy_device *csiphy_dev, void *arg) break; case CSIPHY_CFG: if (copy_from_user(&csiphy_params, - (void *)cdata->cfg.csiphy_params, + (void __user *)cdata->cfg.csiphy_params, sizeof(struct msm_camera_csiphy_params))) { pr_err("%s: %d failed\n", __func__, __LINE__); rc = -EFAULT; diff --git a/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c b/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c index b97156cbd486..6af589e5c230 100644 --- a/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c +++ b/drivers/media/platform/msm/ais/sensor/flash/msm_flash.c @@ -54,7 +54,7 @@ static struct msm_camera_i2c_fn_t msm_sensor_cci_func_tbl = { .i2c_poll = msm_camera_cci_i2c_poll, }; -void msm_torch_brightness_set(struct led_classdev *led_cdev, +static void msm_torch_brightness_set(struct led_classdev *led_cdev, enum led_brightness value) { if (!torch_trigger) { @@ -202,7 +202,7 @@ static int32_t msm_flash_i2c_init( } if (copy_from_user(power_setting_array32, - (void *)flash_init_info->power_setting_array, + (void __user *)flash_init_info->power_setting_array, sizeof(struct msm_sensor_power_setting_array32))) { pr_err("%s copy_from_user failed %d\n", __func__, __LINE__); @@ -248,7 +248,7 @@ static int32_t msm_flash_i2c_init( } else #endif if (copy_from_user(&flash_ctrl->power_setting_array, - (void *)flash_init_info->power_setting_array, + (void __user *)flash_init_info->power_setting_array, sizeof(struct msm_sensor_power_setting_array))) { pr_err("%s copy_from_user failed %d\n", __func__, __LINE__); return -EFAULT; @@ -298,7 +298,8 @@ static int32_t msm_flash_i2c_init( goto msm_flash_i2c_init_fail; } - if (copy_from_user(settings, (void *)flash_init_info->settings, + if (copy_from_user(settings, + (void __user *)flash_init_info->settings, sizeof(struct msm_camera_i2c_reg_setting_array))) { kfree(settings); pr_err("%s copy_from_user failed %d\n", @@ -414,7 +415,7 @@ static int32_t msm_flash_i2c_write_setting_array( if (!settings) return -ENOMEM; - if (copy_from_user(settings, (void *)flash_data->cfg.settings, + if (copy_from_user(settings, (void __user *)flash_data->cfg.settings, sizeof(struct msm_camera_i2c_reg_setting_array))) { kfree(settings); pr_err("%s copy_from_user failed %d\n", __func__, __LINE__); @@ -626,7 +627,7 @@ static int32_t msm_flash_release( } static int32_t msm_flash_config(struct msm_flash_ctrl_t *flash_ctrl, - void __user *argp) + void *argp) { int32_t rc = 0; struct msm_flash_cfg_data_t *flash_data = @@ -701,7 +702,7 @@ static long msm_flash_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct msm_flash_ctrl_t *fctrl = NULL; - void __user *argp = (void __user *)arg; + void *argp = arg; CDBG("Enter\n"); @@ -1038,7 +1039,8 @@ static long msm_flash_subdev_do_ioctl( case CFG_FLASH_INIT: flash_data.cfg.flash_init_info = &flash_init_info; if (copy_from_user(&flash_init_info32, - (void *)compat_ptr(u32->cfg.flash_init_info), + (void __user *) + compat_ptr(u32->cfg.flash_init_info), sizeof(struct msm_flash_init_info_t32))) { pr_err("%s copy_from_user failed %d\n", __func__, __LINE__); diff --git a/drivers/media/platform/msm/ais/sensor/io/msm_camera_cci_i2c.c b/drivers/media/platform/msm/ais/sensor/io/msm_camera_cci_i2c.c index 955be342e8cf..8f2fd0f9e24d 100644 --- a/drivers/media/platform/msm/ais/sensor/io/msm_camera_cci_i2c.c +++ b/drivers/media/platform/msm/ais/sensor/io/msm_camera_cci_i2c.c @@ -23,7 +23,7 @@ int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client, enum msm_camera_i2c_data_type data_type) { int32_t rc = -EFAULT; - unsigned char buf[client->addr_type+data_type]; + unsigned char *buf = NULL; struct msm_camera_cci_ctrl cci_ctrl; if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR @@ -33,6 +33,11 @@ int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client, && data_type != MSM_CAMERA_I2C_WORD_DATA)) return rc; + buf = kzalloc((uint32_t)client->addr_type + (uint32_t)data_type, + GFP_KERNEL); + if (!buf) + return -ENOMEM; + cci_ctrl.cmd = MSM_CCI_I2C_READ; cci_ctrl.cci_info = client->cci_client; cci_ctrl.cfg.cci_i2c_read_cfg.addr = addr; @@ -42,6 +47,8 @@ int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client, rc = v4l2_subdev_call(client->cci_client->cci_subdev, core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); if (rc < 0) { + kfree(buf); + buf = NULL; pr_err("%s: line %d rc = %d\n", __func__, __LINE__, rc); return rc; } @@ -51,6 +58,8 @@ int32_t msm_camera_cci_i2c_read(struct msm_camera_i2c_client *client, else *data = buf[0] << 8 | buf[1]; + kfree(buf); + buf = NULL; S_I2C_DBG("%s addr = 0x%x data: 0x%x\n", __func__, addr, *data); return rc; } diff --git a/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.c b/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.c index 071600ed5221..66300e3f7359 100644 --- a/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.c +++ b/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.c @@ -685,7 +685,7 @@ ERROR2: kfree(array); ERROR1: kfree(ps); - power_setting_size = 0; + power_setting_size = NULL; return rc; } diff --git a/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.h b/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.h index a29ef21274c2..fdeeb4aebf00 100644 --- a/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.h +++ b/drivers/media/platform/msm/ais/sensor/io/msm_camera_dt_util.h @@ -62,6 +62,9 @@ int msm_camera_fill_vreg_params(struct camera_vreg_t *cam_vreg, int msm_camera_pinctrl_init (struct msm_pinctrl_info *sensor_pctrl, struct device *dev); +int msm_cam_sensor_handle_reg_gpio(int seq_val, + struct msm_camera_gpio_conf *gconf, int val); + int32_t msm_sensor_driver_get_gpio_data( struct msm_camera_gpio_conf **gpio_conf, struct device_node *of_node); diff --git a/drivers/media/platform/msm/ais/sensor/io/msm_camera_qup_i2c.c b/drivers/media/platform/msm/ais/sensor/io/msm_camera_qup_i2c.c index 9098b23dbc67..449951f5ffad 100644 --- a/drivers/media/platform/msm/ais/sensor/io/msm_camera_qup_i2c.c +++ b/drivers/media/platform/msm/ais/sensor/io/msm_camera_qup_i2c.c @@ -88,7 +88,8 @@ int32_t msm_camera_qup_i2c_read(struct msm_camera_i2c_client *client, return rc; } - buf = kzalloc(client->addr_type+data_type, GFP_KERNEL); + buf = kzalloc((uint32_t)client->addr_type + (uint32_t)data_type, + GFP_KERNEL); if (!buf) { S_I2C_DBG("%s:%d no memory\n", __func__, __LINE__); return -ENOMEM; @@ -179,7 +180,7 @@ int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client, enum msm_camera_i2c_data_type data_type) { int32_t rc = -EFAULT; - unsigned char buf[client->addr_type+data_type]; + unsigned char *buf = NULL; uint8_t len = 0; if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR @@ -188,6 +189,11 @@ int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client, && data_type != MSM_CAMERA_I2C_WORD_DATA)) return rc; + buf = kzalloc((uint32_t)client->addr_type + (uint32_t)data_type, + GFP_KERNEL); + if (!buf) + return -ENOMEM; + S_I2C_DBG("%s reg addr = 0x%x data type: %d\n", __func__, addr, data_type); if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) { @@ -219,6 +225,9 @@ int32_t msm_camera_qup_i2c_write(struct msm_camera_i2c_client *client, rc = msm_camera_qup_i2c_txdata(client, buf, len); if (rc < 0) S_I2C_DBG("%s fail\n", __func__); + + kfree(buf); + buf = NULL; return rc; } @@ -226,7 +235,7 @@ int32_t msm_camera_qup_i2c_write_seq(struct msm_camera_i2c_client *client, uint32_t addr, uint8_t *data, uint32_t num_byte) { int32_t rc = -EFAULT; - unsigned char buf[client->addr_type+num_byte]; + unsigned char *buf = NULL; uint8_t len = 0, i = 0; if ((client->addr_type != MSM_CAMERA_I2C_BYTE_ADDR @@ -234,6 +243,10 @@ int32_t msm_camera_qup_i2c_write_seq(struct msm_camera_i2c_client *client, || num_byte == 0) return rc; + buf = kzalloc(client->addr_type+num_byte, GFP_KERNEL); + if (!buf) + return -ENOMEM; + S_I2C_DBG("%s reg addr = 0x%x num bytes: %d\n", __func__, addr, num_byte); if (client->addr_type == MSM_CAMERA_I2C_BYTE_ADDR) { @@ -263,6 +276,9 @@ int32_t msm_camera_qup_i2c_write_seq(struct msm_camera_i2c_client *client, rc = msm_camera_qup_i2c_txdata(client, buf, len+num_byte); if (rc < 0) S_I2C_DBG("%s fail\n", __func__); + + kfree(buf); + buf = NULL; return rc; } diff --git a/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.c b/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.c index cd277f0ca0da..d0e27bcc4aac 100644 --- a/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.c +++ b/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.c @@ -513,7 +513,7 @@ int32_t msm_camera_spi_write(struct msm_camera_i2c_client *client, &client->spi_client->cmd_tbl.page_program; uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len; uint16_t len = 0; - char buf[data_type]; + char *buf = NULL; char *tx; int rc = -EINVAL; @@ -524,10 +524,13 @@ int32_t msm_camera_spi_write(struct msm_camera_i2c_client *client, && data_type != MSM_CAMERA_I2C_WORD_DATA)) return rc; S_I2C_DBG("Data: 0x%x\n", data); + buf = kzalloc(data_type, GFP_KERNEL); + if (!buf) + goto NOMEM; len = header_len + (uint8_t)data_type; tx = kmalloc(len, GFP_KERNEL | GFP_DMA); if (!tx) - goto NOMEM; + goto FREEBUF; if (data_type == MSM_CAMERA_I2C_BYTE_DATA) { buf[0] = data; SPIDBG("Byte %d: 0x%x\n", len, buf[0]); @@ -540,6 +543,8 @@ int32_t msm_camera_spi_write(struct msm_camera_i2c_client *client, if (rc < 0) goto ERROR; goto OUT; +FREEBUF: + kfree(buf); NOMEM: pr_err("%s: memory allocation failed\n", __func__); return -ENOMEM; @@ -547,6 +552,7 @@ ERROR: pr_err("%s: error write\n", __func__); OUT: kfree(tx); + kfree(buf); return rc; } int32_t msm_camera_spi_write_table(struct msm_camera_i2c_client *client, @@ -585,7 +591,7 @@ int32_t msm_camera_spi_write_table(struct msm_camera_i2c_client *client, client->addr_type = client_addr_type; return rc; } -uint32_t msm_get_burst_size(struct msm_camera_i2c_reg_array *reg_setting, +static uint32_t msm_get_burst_size(struct msm_camera_i2c_reg_array *reg_setting, uint32_t reg_size, uint32_t index, uint16_t burst_addr) { uint32_t i; @@ -601,7 +607,7 @@ uint32_t msm_get_burst_size(struct msm_camera_i2c_reg_array *reg_setting, } #ifdef SPI_DYNAMIC_ALLOC -int32_t msm_camera_spi_send_burst(struct msm_camera_i2c_client *client, +static int32_t msm_camera_spi_send_burst(struct msm_camera_i2c_client *client, struct msm_camera_i2c_reg_array *reg_setting, uint32_t reg_size, struct msm_camera_burst_info *info, enum msm_camera_i2c_data_type data_type) @@ -677,7 +683,7 @@ fail: return rc; } #else /* SPI_DYNAMIC_ALLOC */ -int32_t msm_camera_spi_send_burst(struct msm_camera_i2c_client *client, +static int32_t msm_camera_spi_send_burst(struct msm_camera_i2c_client *client, struct msm_camera_i2c_reg_array *reg_setting, uint32_t reg_size, struct msm_camera_burst_info *info, enum msm_camera_i2c_data_type data_type) diff --git a/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.h b/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.h index 28aa184ce630..9f87db1dbbfa 100644 --- a/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.h +++ b/drivers/media/platform/msm/ais/sensor/io/msm_camera_spi.h @@ -83,6 +83,14 @@ uint16_t msm_camera_spi_get_hlen(struct msm_camera_spi_inst *inst) return sizeof(inst->opcode) + inst->addr_len + inst->dummy_len; } +int32_t msm_camera_spi_tx_helper(struct msm_camera_i2c_client *client, + struct msm_camera_spi_inst *inst, uint32_t addr, uint8_t *data, + uint32_t num_byte, char *tx, char *rx); + +int32_t msm_camera_spi_tx_read(struct msm_camera_i2c_client *client, + struct msm_camera_spi_inst *inst, uint32_t addr, uint8_t *data, + uint32_t num_byte, char *tx, char *rx); + int32_t msm_camera_spi_read(struct msm_camera_i2c_client *client, uint32_t addr, uint16_t *data, enum msm_camera_i2c_data_type data_type); diff --git a/drivers/media/platform/msm/ais/sensor/ir_cut/msm_ir_cut.c b/drivers/media/platform/msm/ais/sensor/ir_cut/msm_ir_cut.c index bfb960ea862a..68ab4003b666 100644 --- a/drivers/media/platform/msm/ais/sensor/ir_cut/msm_ir_cut.c +++ b/drivers/media/platform/msm/ais/sensor/ir_cut/msm_ir_cut.c @@ -282,7 +282,7 @@ static int32_t msm_ir_cut_handle_init( } static int32_t msm_ir_cut_config(struct msm_ir_cut_ctrl_t *ir_cut_ctrl, - void __user *argp) + void *argp) { int32_t rc = -EINVAL; struct msm_ir_cut_cfg_data_t *ir_cut_data = @@ -327,7 +327,7 @@ static long msm_ir_cut_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct msm_ir_cut_ctrl_t *fctrl = NULL; - void __user *argp = (void __user *)arg; + void *argp = arg; CDBG("Enter\n"); diff --git a/drivers/media/platform/msm/ais/sensor/ir_led/msm_ir_led.c b/drivers/media/platform/msm/ais/sensor/ir_led/msm_ir_led.c index 803bce440ee1..9e200071f9eb 100644 --- a/drivers/media/platform/msm/ais/sensor/ir_led/msm_ir_led.c +++ b/drivers/media/platform/msm/ais/sensor/ir_led/msm_ir_led.c @@ -196,7 +196,7 @@ static int32_t msm_ir_led_handle_init( } static int32_t msm_ir_led_config(struct msm_ir_led_ctrl_t *ir_led_ctrl, - void __user *argp) + void *argp) { int32_t rc = -EINVAL; struct msm_ir_led_cfg_data_t *ir_led_data = @@ -241,7 +241,7 @@ static long msm_ir_led_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct msm_ir_led_ctrl_t *fctrl = NULL; - void __user *argp = (void __user *)arg; + void *argp = arg; struct msm_ir_led_cfg_data_t ir_led_data = {0}; if (!sd) { diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor.c b/drivers/media/platform/msm/ais/sensor/msm_sensor.c index c671ea71d2a7..a276b03e5294 100644 --- a/drivers/media/platform/msm/ais/sensor/msm_sensor.c +++ b/drivers/media/platform/msm/ais/sensor/msm_sensor.c @@ -343,7 +343,7 @@ static long msm_sensor_subdev_ioctl(struct v4l2_subdev *sd, { int rc = 0; struct msm_sensor_ctrl_t *s_ctrl = get_sctrl(sd); - void __user *argp = (void __user *)arg; + void *argp = arg; if (!s_ctrl) { pr_err("%s s_ctrl NULL\n", __func__); @@ -421,7 +421,7 @@ long msm_sensor_subdev_fops_ioctl(struct file *file, } static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, - void __user *argp) + void *argp) { struct sensorb_cfg_data32 *cdata = (struct sensorb_cfg_data32 *)argp; int32_t rc = 0; @@ -498,7 +498,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, } if (copy_from_user(&conf_array32, - (void *)compat_ptr(cdata->cfg.setting), + (void __user *)compat_ptr(cdata->cfg.setting), sizeof(struct msm_camera_i2c_reg_setting32))) { pr_err("%s:%d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -525,7 +525,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, break; } if (copy_from_user(reg_setting, - (void *)(conf_array.reg_setting), + (void __user *)(conf_array.reg_setting), conf_array.size * sizeof(struct msm_camera_i2c_reg_array))) { pr_err("%s:%d failed\n", __func__, __LINE__); @@ -571,7 +571,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, (struct msm_camera_i2c_read_config *) compat_ptr(cdata->cfg.setting); - if (copy_from_user(&read_config, read_config_ptr, + if (copy_from_user(&read_config, (void __user *)read_config_ptr, sizeof(struct msm_camera_i2c_read_config))) { pr_err("%s:%d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -640,7 +640,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, goto DONE; if (copy_from_user(&write_config32, - (void *)compat_ptr(cdata->cfg.setting), + (void __user *)compat_ptr(cdata->cfg.setting), sizeof( struct msm_camera_i2c_array_write_config32))) { pr_err("%s:%d failed\n", __func__, __LINE__); @@ -682,7 +682,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, break; } if (copy_from_user(reg_setting, - (void *)(write_config.conf_array.reg_setting), + (void __user *)(write_config.conf_array.reg_setting), write_config.conf_array.size * sizeof(struct msm_camera_i2c_reg_array))) { pr_err("%s:%d failed\n", __func__, __LINE__); @@ -753,7 +753,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, } if (copy_from_user(&conf_array32, - (void *)compat_ptr(cdata->cfg.setting), + (void __user *)compat_ptr(cdata->cfg.setting), sizeof(struct msm_camera_i2c_seq_reg_setting32))) { pr_err("%s:%d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -780,7 +780,8 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, rc = -ENOMEM; break; } - if (copy_from_user(reg_setting, (void *)conf_array.reg_setting, + if (copy_from_user(reg_setting, + (void __user *)conf_array.reg_setting, conf_array.size * sizeof(struct msm_camera_i2c_seq_reg_array))) { pr_err("%s:%d failed\n", __func__, __LINE__); @@ -863,7 +864,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, goto DONE; if (copy_from_user(&stop_setting32, - (void *)compat_ptr((cdata->cfg.setting)), + (void __user *)compat_ptr((cdata->cfg.setting)), sizeof(struct msm_camera_i2c_reg_setting32))) { pr_err("%s:%d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -890,7 +891,7 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, break; } if (copy_from_user(stop_setting->reg_setting, - (void *)reg_setting, + (void __user *)reg_setting, stop_setting->size * sizeof(struct msm_camera_i2c_reg_array))) { pr_err("%s:%d failed\n", __func__, __LINE__); @@ -949,7 +950,7 @@ DONE: } #endif -int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp) +int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void *argp) { struct sensorb_cfg_data *cdata = (struct sensorb_cfg_data *)argp; int32_t rc = 0; @@ -1026,7 +1027,7 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp) } if (copy_from_user(&conf_array, - (void *)cdata->cfg.setting, + (void __user *)cdata->cfg.setting, sizeof(struct msm_camera_i2c_reg_setting))) { pr_err("%s:%d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -1046,7 +1047,8 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp) rc = -ENOMEM; break; } - if (copy_from_user(reg_setting, (void *)conf_array.reg_setting, + if (copy_from_user(reg_setting, + (void __user *)conf_array.reg_setting, conf_array.size * sizeof(struct msm_camera_i2c_reg_array))) { pr_err("%s:%d failed\n", __func__, __LINE__); @@ -1089,7 +1091,7 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp) read_config_ptr = (struct msm_camera_i2c_read_config *)cdata->cfg.setting; - if (copy_from_user(&read_config, read_config_ptr, + if (copy_from_user(&read_config, (void __user *)read_config_ptr, sizeof(struct msm_camera_i2c_read_config))) { pr_err("%s:%d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -1153,7 +1155,7 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp) goto DONE; if (copy_from_user(&write_config, - (void *)cdata->cfg.setting, + (void __user *)cdata->cfg.setting, sizeof(struct msm_camera_i2c_array_write_config))) { pr_err("%s:%d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -1178,7 +1180,7 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp) break; } if (copy_from_user(reg_setting, - (void *)(write_config.conf_array.reg_setting), + (void __user *)(write_config.conf_array.reg_setting), write_config.conf_array.size * sizeof(struct msm_camera_i2c_reg_array))) { pr_err("%s:%d failed\n", __func__, __LINE__); @@ -1243,7 +1245,7 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp) } if (copy_from_user(&conf_array, - (void *)cdata->cfg.setting, + (void __user *)cdata->cfg.setting, sizeof(struct msm_camera_i2c_seq_reg_setting))) { pr_err("%s:%d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -1265,7 +1267,8 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp) rc = -ENOMEM; break; } - if (copy_from_user(reg_setting, (void *)conf_array.reg_setting, + if (copy_from_user(reg_setting, + (void __user *)conf_array.reg_setting, conf_array.size * sizeof(struct msm_camera_i2c_seq_reg_array))) { pr_err("%s:%d failed\n", __func__, __LINE__); @@ -1349,7 +1352,7 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp) goto DONE; if (copy_from_user(stop_setting, - (void *)cdata->cfg.setting, + (void __user *)cdata->cfg.setting, sizeof(struct msm_camera_i2c_reg_setting))) { pr_err("%s:%d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -1371,7 +1374,7 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp) break; } if (copy_from_user(stop_setting->reg_setting, - (void *)reg_setting, + (void __user *)reg_setting, stop_setting->size * sizeof(struct msm_camera_i2c_reg_array))) { pr_err("%s:%d failed\n", __func__, __LINE__); diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor.h b/drivers/media/platform/msm/ais/sensor/msm_sensor.h index 060383b05170..eacd3b05420c 100644 --- a/drivers/media/platform/msm/ais/sensor/msm_sensor.h +++ b/drivers/media/platform/msm/ais/sensor/msm_sensor.h @@ -94,7 +94,7 @@ struct msm_sensor_ctrl_t { struct msm_sensor_init_t s_init; }; -int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp); +int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void *argp); int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl); diff --git a/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c index 80c15717325c..c02972e5e993 100644 --- a/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c +++ b/drivers/media/platform/msm/ais/sensor/msm_sensor_driver.c @@ -385,7 +385,7 @@ static int32_t msm_sensor_get_pw_settings_compat( pr_err("failed: no memory ps32"); return -ENOMEM; } - if (copy_from_user(ps32, (void *)us_ps, sizeof(*ps32) * size)) { + if (copy_from_user(ps32, (void __user *)us_ps, sizeof(*ps32) * size)) { pr_err("failed: copy_from_user"); kfree(ps32); return -EFAULT; @@ -413,22 +413,18 @@ static int32_t msm_sensor_create_pd_settings(void *setting, #ifdef CONFIG_COMPAT if (is_compat_task()) { - int i = 0; - struct msm_sensor_power_setting32 *power_setting_iter = - (struct msm_sensor_power_setting32 *)compat_ptr(( - (struct msm_camera_sensor_slave_info32 *)setting)-> - power_setting_array.power_setting); - - for (i = 0; i < size_down; i++) { - pd[i].config_val = power_setting_iter[i].config_val; - pd[i].delay = power_setting_iter[i].delay; - pd[i].seq_type = power_setting_iter[i].seq_type; - pd[i].seq_val = power_setting_iter[i].seq_val; + rc = msm_sensor_get_pw_settings_compat( + pd, pu, size_down); + if (rc < 0) { + pr_err("failed"); + return -EFAULT; } } else #endif { - if (copy_from_user(pd, (void *)pu, sizeof(*pd) * size_down)) { + if (copy_from_user(pd, + (void __user *)pu, + sizeof(*pd) * size_down)) { pr_err("failed: copy_from_user"); return -EFAULT; } @@ -480,7 +476,8 @@ static int32_t msm_sensor_get_power_down_settings(void *setting, } } else #endif - if (copy_from_user(pd, (void *)slave_info->power_setting_array. + if (copy_from_user(pd, + (void __user *)slave_info->power_setting_array. power_down_setting, sizeof(*pd) * size_down)) { pr_err("failed: copy_from_user"); kfree(pd); @@ -546,7 +543,8 @@ static int32_t msm_sensor_get_power_up_settings(void *setting, #endif { if (copy_from_user(pu, - (void *)slave_info->power_setting_array.power_setting, + (void __user *) + slave_info->power_setting_array.power_setting, sizeof(*pu) * size)) { pr_err("failed: copy_from_user"); kfree(pu); @@ -659,7 +657,7 @@ int32_t msm_sensor_driver_probe(void *setting, rc = -ENOMEM; goto free_slave_info; } - if (copy_from_user((void *)slave_info32, setting, + if (copy_from_user((void *)slave_info32, (void __user *)setting, sizeof(*slave_info32))) { pr_err("failed: copy_from_user"); rc = -EFAULT; @@ -710,7 +708,7 @@ int32_t msm_sensor_driver_probe(void *setting, #endif { if (copy_from_user(slave_info, - (void *)setting, sizeof(*slave_info))) { + (void __user *)setting, sizeof(*slave_info))) { pr_err("failed: copy_from_user"); rc = -EFAULT; goto free_slave_info; diff --git a/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c b/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c index f3147b127438..28a5402a4359 100644 --- a/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c +++ b/drivers/media/platform/msm/ais/sensor/ois/msm_ois.c @@ -382,7 +382,7 @@ static int32_t msm_ois_control(struct msm_ois_ctrl_t *o_ctrl, return -EFAULT; } if (copy_from_user(settings, - (void *)set_info->ois_params.settings, + (void __user *)set_info->ois_params.settings, set_info->ois_params.setting_size * sizeof(struct reg_settings_ois_t))) { kfree(settings); @@ -407,7 +407,7 @@ static int32_t msm_ois_control(struct msm_ois_ctrl_t *o_ctrl, static int32_t msm_ois_config(struct msm_ois_ctrl_t *o_ctrl, - void __user *argp) + void *argp) { struct msm_ois_cfg_data *cdata = (struct msm_ois_cfg_data *)argp; @@ -449,7 +449,7 @@ static int32_t msm_ois_config(struct msm_ois_ctrl_t *o_ctrl, } else #endif if (copy_from_user(&conf_array, - (void *)cdata->cfg.settings, + (void __user *)cdata->cfg.settings, sizeof(struct msm_camera_i2c_seq_reg_setting))) { pr_err("%s:%d failed\n", __func__, __LINE__); rc = -EFAULT; @@ -470,7 +470,8 @@ static int32_t msm_ois_config(struct msm_ois_ctrl_t *o_ctrl, rc = -ENOMEM; break; } - if (copy_from_user(reg_setting, (void *)conf_array.reg_setting, + if (copy_from_user(reg_setting, + (void __user *)conf_array.reg_setting, conf_array.size * sizeof(struct msm_camera_i2c_seq_reg_array))) { pr_err("%s:%d failed\n", __func__, __LINE__); @@ -495,7 +496,7 @@ static int32_t msm_ois_config(struct msm_ois_ctrl_t *o_ctrl, } static int32_t msm_ois_config_download(struct msm_ois_ctrl_t *o_ctrl, - void __user *argp) + void *argp) { struct msm_ois_cfg_download_data *cdata = (struct msm_ois_cfg_download_data *)argp; @@ -606,7 +607,7 @@ static long msm_ois_subdev_ioctl(struct v4l2_subdev *sd, { int rc; struct msm_ois_ctrl_t *o_ctrl = v4l2_get_subdevdata(sd); - void __user *argp = (void __user *)arg; + void *argp = arg; CDBG("Enter\n"); CDBG("%s:%d o_ctrl %pK argp %pK\n", __func__, __LINE__, o_ctrl, argp); @@ -805,7 +806,7 @@ static long msm_ois_subdev_do_ioctl( break; case CFG_OIS_I2C_WRITE_SEQ_TABLE: if (copy_from_user(&settings32, - (void *)compat_ptr(u32->cfg.settings), + (void __user *)compat_ptr(u32->cfg.settings), sizeof( struct msm_camera_i2c_seq_reg_setting32))) { pr_err("copy_from_user failed\n"); diff --git a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c index 2030755d59d0..7d9edde62c1b 100644 --- a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c +++ b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c @@ -1360,11 +1360,12 @@ int cam_smmu_set_attr(int handle, uint32_t flags, int32_t *data) /* set attributes */ ret = iommu_domain_set_attr(domain, cb->attr, (void *)data); if (ret < 0) { + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); pr_err("Error: set attr\n"); return -ENODEV; } } else { - return -EINVAL; + ret = -EINVAL; } mutex_unlock(&iommu_cb_set.cb_info[idx].lock); return ret; diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c index c5f5a12d08eb..a04d7ca73fe1 100644 --- a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c +++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c @@ -749,9 +749,13 @@ static int msm_fd_s_fmt_vid_out(struct file *file, static int msm_fd_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req) { + int ret; struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); - return vb2_reqbufs(&ctx->vb2_q, req); + mutex_lock(&ctx->fd_device->recovery_lock); + ret = vb2_reqbufs(&ctx->vb2_q, req); + mutex_unlock(&ctx->fd_device->recovery_lock); + return ret; } /* @@ -763,9 +767,14 @@ static int msm_fd_reqbufs(struct file *file, static int msm_fd_qbuf(struct file *file, void *fh, struct v4l2_buffer *pb) { + int ret; struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); - return vb2_qbuf(&ctx->vb2_q, pb); + mutex_lock(&ctx->fd_device->recovery_lock); + ret = vb2_qbuf(&ctx->vb2_q, pb); + mutex_unlock(&ctx->fd_device->recovery_lock); + return ret; + } /* @@ -777,9 +786,13 @@ static int msm_fd_qbuf(struct file *file, void *fh, static int msm_fd_dqbuf(struct file *file, void *fh, struct v4l2_buffer *pb) { + int ret; struct fd_ctx *ctx = msm_fd_ctx_from_fh(fh); - return vb2_dqbuf(&ctx->vb2_q, pb, file->f_flags & O_NONBLOCK); + mutex_lock(&ctx->fd_device->recovery_lock); + ret = vb2_dqbuf(&ctx->vb2_q, pb, file->f_flags & O_NONBLOCK); + mutex_unlock(&ctx->fd_device->recovery_lock); + return ret; } /* diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c index 5dbed80f5b85..8f3cffb4c3da 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_buf_mgr.c @@ -1166,10 +1166,15 @@ int msm_isp_smmu_attach(struct msm_isp_buf_mgr *buf_mgr, { struct msm_vfe_smmu_attach_cmd *cmd = arg; int rc = 0; + int32_t stall_disable = 1; pr_debug("%s: cmd->security_mode : %d\n", __func__, cmd->security_mode); + mutex_lock(&buf_mgr->lock); if (cmd->iommu_attach_mode == IOMMU_ATTACH) { + /* disable smmu stall on fault */ + cam_smmu_set_attr(buf_mgr->iommu_hdl, + DOMAIN_ATTR_CB_STALL_DISABLE, &stall_disable); /* * Call hypervisor thru scm call to notify secure or * non-secure mode diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h index d64cee834bea..1f1435fe282e 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h @@ -456,7 +456,7 @@ struct msm_vfe_axi_stream { uint32_t runtime_output_format; enum msm_stream_rdi_input_type rdi_input_type; struct msm_isp_sw_framskip sw_skip; - uint8_t sw_ping_pong_bit; + int8_t sw_ping_pong_bit; struct vfe_device *vfe_dev[MAX_VFE]; int num_isp; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index 03d1b3c22d61..6515e3d6ecbc 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -1360,6 +1360,7 @@ void msm_vfe47_cfg_camif(struct vfe_device *vfe_dev, { uint16_t first_pixel, last_pixel, first_line, last_line; struct msm_vfe_camif_cfg *camif_cfg = &pix_cfg->camif_cfg; + struct msm_vfe_testgen_cfg *testgen_cfg = &pix_cfg->testgen_cfg; uint32_t val, subsample_period, subsample_pattern; uint32_t irq_sub_period = 32; uint32_t frame_sub_period = 32; @@ -1383,8 +1384,15 @@ void msm_vfe47_cfg_camif(struct vfe_device *vfe_dev, subsample_period = camif_cfg->subsample_cfg.irq_subsample_period; subsample_pattern = camif_cfg->subsample_cfg.irq_subsample_pattern; - msm_camera_io_w((camif_cfg->lines_per_frame - 1) << 16 | - (camif_cfg->pixels_per_line - 1), vfe_dev->vfe_base + 0x484); + if (pix_cfg->input_mux == TESTGEN) + msm_camera_io_w((testgen_cfg->lines_per_frame - 1) << 16 | + (testgen_cfg->pixels_per_line - 1), + vfe_dev->vfe_base + 0x484); + else + msm_camera_io_w((camif_cfg->lines_per_frame - 1) << 16 | + (camif_cfg->pixels_per_line - 1), + vfe_dev->vfe_base + 0x484); + if (bus_sub_en) { val = msm_camera_io_r(vfe_dev->vfe_base + 0x47C); val &= 0xFFFFFFDF; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index f9809615b407..0e0f10e7fbb5 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -1259,7 +1259,7 @@ void msm_isp_get_avtimer_ts( int msm_isp_request_axi_stream(struct vfe_device *vfe_dev, void *arg) { - int rc = 0, i; + int rc = 0, i = 0; uint32_t io_format = 0; struct msm_vfe_axi_stream_request_cmd *stream_cfg_cmd = arg; struct msm_vfe_axi_stream *stream_info; @@ -1395,7 +1395,7 @@ done: int msm_isp_release_axi_stream(struct vfe_device *vfe_dev, void *arg) { - int rc = 0, i; + int rc = 0, i = 0; struct msm_vfe_axi_stream_release_cmd *stream_release_cmd = arg; struct msm_vfe_axi_stream *stream_info; struct msm_vfe_axi_stream_cfg_cmd stream_cfg; @@ -1699,6 +1699,9 @@ static int msm_isp_update_deliver_count(struct vfe_device *vfe_dev, rc = -EINVAL; goto done; } else { + /*After wm reload, we get bufdone for ping buffer*/ + if (stream_info->sw_ping_pong_bit == -1) + stream_info->sw_ping_pong_bit = 0; stream_info->undelivered_request_cnt--; if (pingpong_bit != stream_info->sw_ping_pong_bit) { pr_err("%s:%d ping pong bit actual %d sw %d\n", @@ -2296,6 +2299,8 @@ static void msm_isp_input_disable(struct vfe_device *vfe_dev, int cmd_type) ms_res->src_info[src_info->dual_hw_ms_info.index] = NULL; ms_res->num_src--; + if (ms_res->num_src == 0) + ms_res->dual_sync_mode = MSM_ISP_DUAL_CAM_ASYNC; src_info->dual_hw_ms_info.sync_state = MSM_ISP_DUAL_CAM_ASYNC; src_info->dual_hw_type = DUAL_NONE; @@ -2480,6 +2485,12 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg) rc = -1; return rc; } + if (ab_ib_vote->num_src >= VFE_AXI_SRC_MAX) { + pr_err("%s: ab_ib_vote num_src is exceeding limit\n", + __func__); + rc = -1; + return rc; + } if (ab_ib_vote->lpm_mode) { for (i = 0; i < ab_ib_vote->num_src; i++) { stream_info = @@ -2492,7 +2503,8 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg) intf = SRC_TO_INTF(stream_info->stream_src); vfe_dev->axi_data.src_info[intf].lpm = ab_ib_vote->lpm_mode; - if (stream_info->lpm_mode) { + if (stream_info->lpm_mode || + stream_info->state == INACTIVE) { spin_unlock_irqrestore(&stream_info->lock, flags); continue; @@ -2512,7 +2524,8 @@ int msm_isp_ab_ib_update_lpm_mode(struct vfe_device *vfe_dev, void *arg) intf = SRC_TO_INTF(stream_info->stream_src); vfe_dev->axi_data.src_info[intf].lpm = ab_ib_vote->lpm_mode; - if (stream_info->lpm_mode == 0) { + if (stream_info->lpm_mode == 0 || + stream_info->state == INACTIVE) { spin_unlock_irqrestore(&stream_info->lock, flags); continue; @@ -3469,7 +3482,14 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, spin_lock_irqsave(&stream_info->lock, flags); vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); - if (stream_info->undelivered_request_cnt == 1) { + /* + * When wm reloaded, pingpong status register would be stale, pingpong + * status would be updated only after AXI_DONE interrupt processed. + * So, we should avoid reading value from pingpong status register + * until buf_done happens for ping buffer. + */ + if ((stream_info->undelivered_request_cnt == 1) && + (stream_info->sw_ping_pong_bit != -1)) { pingpong_status = vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status( vfe_dev); @@ -3542,10 +3562,25 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, stream_info->vfe_dev[k]->vfe_base, wm_mask); } - stream_info->sw_ping_pong_bit = 0; + /* + * sw_ping_pong_bit is updated only when AXI_DONE. + * so now reset this bit to -1. + */ + stream_info->sw_ping_pong_bit = -1; } else if (stream_info->undelivered_request_cnt == 2) { - rc = msm_isp_cfg_ping_pong_address( - stream_info, pingpong_status); + if (stream_info->sw_ping_pong_bit == -1) { + /* + * This means wm is reloaded & ping buffer is + * already configured. And AXI_DONE for ping + * is still pending. So, config pong buffer + * now. + */ + rc = msm_isp_cfg_ping_pong_address(stream_info, + VFE_PONG_FLAG); + } else { + rc = msm_isp_cfg_ping_pong_address( + stream_info, pingpong_status); + } if (rc) { stream_info->undelivered_request_cnt--; spin_unlock_irqrestore(&stream_info->lock, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c index ee695bf5dfd9..f0831e64f250 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c @@ -267,7 +267,9 @@ static int32_t msm_isp_stats_configure(struct vfe_device *vfe_dev, int result = 0; memset(&buf_event, 0, sizeof(struct msm_isp_event_data)); - buf_event.timestamp = ts->buf_time; + buf_event.timestamp = ts->event_time; + buf_event.mono_timestamp = ts->buf_time; + buf_event.frame_id = vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id; pingpong_status = vfe_dev->hw_info-> vfe_ops.stats_ops.get_pingpong_status(vfe_dev); @@ -1263,7 +1265,7 @@ int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg) &update_cmd->update_info[i]; /*check array reference bounds*/ if (STATS_IDX(update_info->stream_handle) - > vfe_dev->hw_info->stats_hw_info->num_stats_type) { + >= vfe_dev->hw_info->stats_hw_info->num_stats_type) { pr_err("%s: stats idx %d out of bound!", __func__, STATS_IDX(update_info->stream_handle)); return -EINVAL; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index f19e6dd1cb01..d30d8022b7ab 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -479,8 +479,12 @@ static int msm_isp_cfg_pix(struct vfe_device *vfe_dev, if (input_cfg->d.pix_cfg.input_mux == CAMIF || input_cfg->d.pix_cfg.input_mux == TESTGEN) { - vfe_dev->axi_data.src_info[VFE_PIX_0].width = - input_cfg->d.pix_cfg.camif_cfg.pixels_per_line; + if (input_cfg->d.pix_cfg.input_mux == CAMIF) + vfe_dev->axi_data.src_info[VFE_PIX_0].width = + input_cfg->d.pix_cfg.camif_cfg.pixels_per_line; + if (input_cfg->d.pix_cfg.input_mux == TESTGEN) + vfe_dev->axi_data.src_info[VFE_PIX_0].width = + input_cfg->d.pix_cfg.testgen_cfg.pixels_per_line; if (input_cfg->d.pix_cfg.camif_cfg.subsample_cfg. sof_counter_step > 0) { vfe_dev->axi_data.src_info[VFE_PIX_0]. diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c index 24e3223a79d0..3f900ded090a 100644 --- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c +++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c @@ -86,6 +86,12 @@ static void msm_ispif_io_dump_reg(struct ispif_device *ispif) { if (!ispif->enb_dump_reg) return; + + if (!ispif->base) { + pr_err("%s: null pointer for the ispif base\n", __func__); + return; + } + msm_camera_io_dump(ispif->base, 0x250, 0); } @@ -621,7 +627,7 @@ static int msm_ispif_reset(struct ispif_device *ispif) ispif->base + ISPIF_VFE_m_INTF_CMD_0(i)); msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY, ispif->base + ISPIF_VFE_m_INTF_CMD_1(i)); - pr_debug("%s: base %lx", __func__, (unsigned long)ispif->base); + pr_debug("%s: base %pK", __func__, ispif->base); msm_camera_io_w(0, ispif->base + ISPIF_VFE_m_PIX_INTF_n_CID_MASK(i, 0)); msm_camera_io_w(0, ispif->base + @@ -1021,6 +1027,11 @@ static void msm_ispif_config_stereo(struct ispif_device *ispif, for (i = 0; i < params->num; i++) { vfe_intf = params->entries[i].vfe_intf; + if (!msm_ispif_is_intf_valid(ispif->csid_version, vfe_intf)) { + pr_err("%s: invalid interface type %d\n", __func__, + vfe_intf); + return; + } if (params->entries[i].intftype == PIX0 && params->stereo_enable && params->right_entries[i].csid < CSID_MAX && diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c index f95cc37f5c2c..9cb7d5299ef8 100644 --- a/drivers/media/platform/msm/camera_v2/msm.c +++ b/drivers/media/platform/msm/camera_v2/msm.c @@ -32,7 +32,6 @@ #include "cam_hw_ops.h" #include <media/msmb_generic_buf_mgr.h> - static struct v4l2_device *msm_v4l2_dev; static struct list_head ordered_sd_list; @@ -149,7 +148,7 @@ typedef int (*msm_queue_find_func)(void *d1, void *d2); #define msm_queue_find(queue, type, member, func, data) ({\ unsigned long flags; \ struct msm_queue_head *__q = (queue); \ - type *node = 0; \ + type *node = NULL; \ typeof(node) __ret = NULL; \ msm_queue_find_func __f = (func); \ spin_lock_irqsave(&__q->lock, flags); \ @@ -279,22 +278,47 @@ void msm_delete_stream(unsigned int session_id, unsigned int stream_id) struct msm_session *session = NULL; struct msm_stream *stream = NULL; unsigned long flags; + int try_count = 0; session = msm_queue_find(msm_session_q, struct msm_session, list, __msm_queue_find_session, &session_id); + if (!session) return; - stream = msm_queue_find(&session->stream_q, struct msm_stream, - list, __msm_queue_find_stream, &stream_id); - if (!stream) - return; - spin_lock_irqsave(&(session->stream_q.lock), flags); - list_del_init(&stream->list); - session->stream_q.len--; - kfree(stream); - stream = NULL; - spin_unlock_irqrestore(&(session->stream_q.lock), flags); + while (1) { + + if (try_count > 5) { + pr_err("%s : not able to delete stream %d\n", + __func__, __LINE__); + break; + } + + write_lock(&session->stream_rwlock); + try_count++; + stream = msm_queue_find(&session->stream_q, struct msm_stream, + list, __msm_queue_find_stream, &stream_id); + + if (!stream) { + write_unlock(&session->stream_rwlock); + return; + } + + if (msm_vb2_get_stream_state(stream) != 1) { + write_unlock(&session->stream_rwlock); + continue; + } + + spin_lock_irqsave(&(session->stream_q.lock), flags); + list_del_init(&stream->list); + session->stream_q.len--; + kfree(stream); + stream = NULL; + spin_unlock_irqrestore(&(session->stream_q.lock), flags); + write_unlock(&session->stream_rwlock); + break; + } + } EXPORT_SYMBOL(msm_delete_stream); @@ -444,6 +468,7 @@ int msm_create_session(unsigned int session_id, struct video_device *vdev) mutex_init(&session->lock); mutex_init(&session->lock_q); mutex_init(&session->close_lock); + rwlock_init(&session->stream_rwlock); if (gpu_limit) { session->sysfs_pwr_limit = kgsl_pwr_limits_add(KGSL_DEVICE_3D0); @@ -1048,17 +1073,25 @@ static struct v4l2_file_operations msm_fops = { #endif }; -struct msm_stream *msm_get_stream(unsigned int session_id, - unsigned int stream_id) +struct msm_session *msm_get_session(unsigned int session_id) { struct msm_session *session; - struct msm_stream *stream; session = msm_queue_find(msm_session_q, struct msm_session, list, __msm_queue_find_session, &session_id); if (!session) return ERR_PTR(-EINVAL); + return session; +} +EXPORT_SYMBOL(msm_get_session); + + +struct msm_stream *msm_get_stream(struct msm_session *session, + unsigned int stream_id) +{ + struct msm_stream *stream; + stream = msm_queue_find(&session->stream_q, struct msm_stream, list, __msm_queue_find_stream, &stream_id); @@ -1115,6 +1148,34 @@ struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q) } EXPORT_SYMBOL(msm_get_stream_from_vb2q); +struct msm_session *msm_get_session_from_vb2q(struct vb2_queue *q) +{ + struct msm_session *session; + struct msm_stream *stream; + unsigned long flags1; + unsigned long flags2; + + spin_lock_irqsave(&msm_session_q->lock, flags1); + list_for_each_entry(session, &(msm_session_q->list), list) { + spin_lock_irqsave(&(session->stream_q.lock), flags2); + list_for_each_entry( + stream, &(session->stream_q.list), list) { + if (stream->vb2_q == q) { + spin_unlock_irqrestore + (&(session->stream_q.lock), flags2); + spin_unlock_irqrestore + (&msm_session_q->lock, flags1); + return session; + } + } + spin_unlock_irqrestore(&(session->stream_q.lock), flags2); + } + spin_unlock_irqrestore(&msm_session_q->lock, flags1); + return NULL; +} +EXPORT_SYMBOL(msm_get_session_from_vb2q); + + #ifdef CONFIG_COMPAT long msm_copy_camera_private_ioctl_args(unsigned long arg, struct msm_camera_private_ioctl_arg *k_ioctl, diff --git a/drivers/media/platform/msm/camera_v2/msm.h b/drivers/media/platform/msm/camera_v2/msm.h index 7474cb119147..dce47bc7249c 100644 --- a/drivers/media/platform/msm/camera_v2/msm.h +++ b/drivers/media/platform/msm/camera_v2/msm.h @@ -111,6 +111,7 @@ struct msm_session { struct mutex lock; struct mutex lock_q; struct mutex close_lock; + rwlock_t stream_rwlock; struct kgsl_pwr_limit *sysfs_pwr_limit; }; @@ -129,11 +130,13 @@ int msm_create_stream(unsigned int session_id, void msm_delete_stream(unsigned int session_id, unsigned int stream_id); int msm_create_command_ack_q(unsigned int session_id, unsigned int stream_id); void msm_delete_command_ack_q(unsigned int session_id, unsigned int stream_id); -struct msm_stream *msm_get_stream(unsigned int session_id, +struct msm_session *msm_get_session(unsigned int session_id); +struct msm_stream *msm_get_stream(struct msm_session *session, unsigned int stream_id); struct vb2_queue *msm_get_stream_vb2q(unsigned int session_id, unsigned int stream_id); struct msm_stream *msm_get_stream_from_vb2q(struct vb2_queue *q); +struct msm_session *msm_get_session_from_vb2q(struct vb2_queue *q); struct msm_session *msm_session_find(unsigned int session_id); #ifdef CONFIG_COMPAT long msm_copy_camera_private_ioctl_args(unsigned long arg, diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c index c779ee46c19a..ba9b4df6bf22 100644 --- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c +++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -44,17 +44,25 @@ static int msm_vb2_queue_setup(struct vb2_queue *q, int msm_vb2_buf_init(struct vb2_buffer *vb) { struct msm_stream *stream; + struct msm_session *session; struct msm_vb2_buffer *msm_vb2_buf; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + session = msm_get_session_from_vb2q(vb->vb2_queue); + if (IS_ERR_OR_NULL(session)) + return -EINVAL; + + read_lock(&session->stream_rwlock); + stream = msm_get_stream_from_vb2q(vb->vb2_queue); if (!stream) { pr_err("%s: Couldn't find stream\n", __func__); + read_unlock(&session->stream_rwlock); return -EINVAL; } msm_vb2_buf = container_of(vbuf, struct msm_vb2_buffer, vb2_v4l2_buf); msm_vb2_buf->in_freeq = 0; - + read_unlock(&session->stream_rwlock); return 0; } @@ -62,6 +70,7 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb) { struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; + struct msm_session *session; unsigned long flags; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); @@ -71,21 +80,30 @@ static void msm_vb2_buf_queue(struct vb2_buffer *vb) return; } + session = msm_get_session_from_vb2q(vb->vb2_queue); + if (IS_ERR_OR_NULL(session)) + return; + + read_lock(&session->stream_rwlock); + stream = msm_get_stream_from_vb2q(vb->vb2_queue); if (!stream) { pr_err("%s:%d] NULL stream", __func__, __LINE__); + read_unlock(&session->stream_rwlock); return; } spin_lock_irqsave(&stream->stream_lock, flags); list_add_tail(&msm_vb2->list, &stream->queued_list); spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); } static void msm_vb2_buf_finish(struct vb2_buffer *vb) { struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; + struct msm_session *session; unsigned long flags; struct msm_vb2_buffer *msm_vb2_entry, *temp; struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); @@ -96,9 +114,16 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb) return; } + session = msm_get_session_from_vb2q(vb->vb2_queue); + if (IS_ERR_OR_NULL(session)) + return; + + read_lock(&session->stream_rwlock); + stream = msm_get_stream_from_vb2q(vb->vb2_queue); if (!stream) { pr_err("%s:%d] NULL stream", __func__, __LINE__); + read_unlock(&session->stream_rwlock); return; } @@ -111,6 +136,7 @@ static void msm_vb2_buf_finish(struct vb2_buffer *vb) } } spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return; } @@ -118,12 +144,20 @@ static void msm_vb2_stop_stream(struct vb2_queue *q) { struct msm_vb2_buffer *msm_vb2, *temp; struct msm_stream *stream; + struct msm_session *session; unsigned long flags; struct vb2_v4l2_buffer *vb2_v4l2_buf; + session = msm_get_session_from_vb2q(q); + if (IS_ERR_OR_NULL(session)) + return; + + read_lock(&session->stream_rwlock); + stream = msm_get_stream_from_vb2q(q); if (!stream) { pr_err_ratelimited("%s:%d] NULL stream", __func__, __LINE__); + read_unlock(&session->stream_rwlock); return; } @@ -143,7 +177,27 @@ static void msm_vb2_stop_stream(struct vb2_queue *q) msm_vb2->in_freeq = 0; } spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); +} + +int msm_vb2_get_stream_state(struct msm_stream *stream) +{ + struct msm_vb2_buffer *msm_vb2, *temp; + unsigned long flags; + int rc = 1; + + spin_lock_irqsave(&stream->stream_lock, flags); + list_for_each_entry_safe(msm_vb2, temp, &(stream->queued_list), list) { + if (msm_vb2->in_freeq != 0) { + rc = 0; + break; + } + } + spin_unlock_irqrestore(&stream->stream_lock, flags); + return rc; } +EXPORT_SYMBOL(msm_vb2_get_stream_state); + static struct vb2_ops msm_vb2_get_q_op = { .queue_setup = msm_vb2_queue_setup, @@ -198,14 +252,23 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id, unsigned int stream_id) { struct msm_stream *stream; + struct msm_session *session; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; struct msm_vb2_buffer *msm_vb2 = NULL; unsigned long flags; - stream = msm_get_stream(session_id, stream_id); - if (IS_ERR_OR_NULL(stream)) + session = msm_get_session(session_id); + if (IS_ERR_OR_NULL(session)) return NULL; + read_lock(&session->stream_rwlock); + + stream = msm_get_stream(session, stream_id); + if (IS_ERR_OR_NULL(stream)) { + read_unlock(&session->stream_rwlock); + return NULL; + } + spin_lock_irqsave(&stream->stream_lock, flags); if (!stream->vb2_q) { @@ -228,6 +291,7 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf(int session_id, vb2_v4l2_buf = NULL; end: spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return vb2_v4l2_buf; } @@ -235,13 +299,23 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id, unsigned int stream_id, uint32_t index) { struct msm_stream *stream; + struct msm_session *session; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; struct msm_vb2_buffer *msm_vb2 = NULL; unsigned long flags; - stream = msm_get_stream(session_id, stream_id); - if (IS_ERR_OR_NULL(stream)) + session = msm_get_session(session_id); + if (IS_ERR_OR_NULL(session)) + return NULL; + + read_lock(&session->stream_rwlock); + + stream = msm_get_stream(session, stream_id); + + if (IS_ERR_OR_NULL(stream)) { + read_unlock(&session->stream_rwlock); return NULL; + } spin_lock_irqsave(&stream->stream_lock, flags); @@ -263,6 +337,7 @@ static struct vb2_v4l2_buffer *msm_vb2_get_buf_by_idx(int session_id, vb2_v4l2_buf = NULL; end: spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return vb2_v4l2_buf; } @@ -270,14 +345,24 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id, unsigned int stream_id) { struct msm_stream *stream; + struct msm_session *session; struct msm_vb2_buffer *msm_vb2; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; int rc = 0; unsigned long flags; - stream = msm_get_stream(session_id, stream_id); - if (IS_ERR_OR_NULL(stream)) + + session = msm_get_session(session_id); + if (IS_ERR_OR_NULL(session)) return -EINVAL; + read_lock(&session->stream_rwlock); + + stream = msm_get_stream(session, stream_id); + if (IS_ERR_OR_NULL(stream)) { + read_unlock(&session->stream_rwlock); + return -EINVAL; + } + spin_lock_irqsave(&stream->stream_lock, flags); if (vb) { list_for_each_entry(msm_vb2, &(stream->queued_list), list) { @@ -305,6 +390,7 @@ static int msm_vb2_put_buf(struct vb2_v4l2_buffer *vb, int session_id, rc = -EINVAL; } spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return rc; } @@ -315,12 +401,22 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id, unsigned long flags; struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; + struct msm_session *session; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; int rc = 0; - stream = msm_get_stream(session_id, stream_id); - if (IS_ERR_OR_NULL(stream)) + session = msm_get_session(session_id); + if (IS_ERR_OR_NULL(session)) return -EINVAL; + + read_lock(&session->stream_rwlock); + + stream = msm_get_stream(session, stream_id); + if (IS_ERR_OR_NULL(stream)) { + read_unlock(&session->stream_rwlock); + return -EINVAL; + } + spin_lock_irqsave(&stream->stream_lock, flags); if (vb) { list_for_each_entry(msm_vb2, &(stream->queued_list), list) { @@ -352,6 +448,7 @@ static int msm_vb2_buf_done(struct vb2_v4l2_buffer *vb, int session_id, rc = -EINVAL; } spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return rc; } @@ -359,15 +456,24 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id, uint32_t index) { struct msm_stream *stream; + struct msm_session *session; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; struct msm_vb2_buffer *msm_vb2 = NULL; unsigned long flags; long rc = -EINVAL; - stream = msm_get_stream(session_id, stream_id); - if (IS_ERR_OR_NULL(stream)) + session = msm_get_session(session_id); + if (IS_ERR_OR_NULL(session)) return rc; + read_lock(&session->stream_rwlock); + + stream = msm_get_stream(session, stream_id); + if (IS_ERR_OR_NULL(stream)) { + read_unlock(&session->stream_rwlock); + return -EINVAL; + } + spin_lock_irqsave(&stream->stream_lock, flags); if (!stream->vb2_q) { @@ -393,6 +499,7 @@ long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id, end: spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return rc; } EXPORT_SYMBOL(msm_vb2_return_buf_by_idx); @@ -402,11 +509,21 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id) unsigned long flags; struct msm_vb2_buffer *msm_vb2; struct msm_stream *stream; + struct msm_session *session; struct vb2_v4l2_buffer *vb2_v4l2_buf = NULL; - stream = msm_get_stream(session_id, stream_id); - if (IS_ERR_OR_NULL(stream)) + session = msm_get_session(session_id); + if (IS_ERR_OR_NULL(session)) + return -EINVAL; + + read_lock(&session->stream_rwlock); + + stream = msm_get_stream(session, stream_id); + if (IS_ERR_OR_NULL(stream)) { + read_unlock(&session->stream_rwlock); return -EINVAL; + } + spin_lock_irqsave(&stream->stream_lock, flags); list_for_each_entry(msm_vb2, &(stream->queued_list), list) { vb2_v4l2_buf = &(msm_vb2->vb2_v4l2_buf); @@ -415,6 +532,7 @@ static int msm_vb2_flush_buf(int session_id, unsigned int stream_id) msm_vb2->in_freeq = 0; } spin_unlock_irqrestore(&stream->stream_lock, flags); + read_unlock(&session->stream_rwlock); return 0; } diff --git a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h index 53511d5416d7..c65cb58128d9 100644 --- a/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h +++ b/drivers/media/platform/msm/camera_v2/msm_vb2/msm_vb2.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -67,5 +67,6 @@ struct vb2_mem_ops *msm_vb2_get_q_mem_ops(void); int msm_vb2_request_cb(struct msm_sd_req_vb2_q *req_sd); long msm_vb2_return_buf_by_idx(int session_id, unsigned int stream_id, uint32_t index); +int msm_vb2_get_stream_state(struct msm_stream *stream); #endif /*_MSM_VB_H */ diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index cd48f871eb79..bc844bb87db5 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -573,7 +573,10 @@ static int32_t msm_actuator_move_focus( CDBG("called, dir %d, num_steps %d\n", dir, num_steps); - if (dest_step_pos == a_ctrl->curr_step_pos) + if ((dest_step_pos == a_ctrl->curr_step_pos) || + ((dest_step_pos <= a_ctrl->total_steps) && + (a_ctrl->step_position_table[dest_step_pos] == + a_ctrl->step_position_table[a_ctrl->curr_step_pos]))) return rc; if ((sign_dir > MSM_ACTUATOR_MOVE_SIGNED_NEAR) || diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c index 75043e1b0427..7dda92510879 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c +++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c @@ -1602,6 +1602,12 @@ static int32_t msm_cci_write(struct v4l2_subdev *sd, return rc; } + if (cci_dev->cci_state != CCI_STATE_ENABLED) { + pr_err("%s invalid cci state %d\n", + __func__, cci_dev->cci_state); + return -EINVAL; + } + if (c_ctrl->cci_info->cci_i2c_master >= MASTER_MAX || c_ctrl->cci_info->cci_i2c_master < 0) { pr_err("%s:%d Invalid I2C master addr\n", __func__, __LINE__); diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c index 5376e1e4b6a4..491b8d31935a 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c +++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_flash.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1144,13 +1144,13 @@ static long msm_flash_subdev_do_ioctl( sd = vdev_to_v4l2_subdev(vdev); u32 = (struct msm_flash_cfg_data_t32 *)arg; - flash_data.cfg_type = u32->cfg_type; - for (i = 0; i < MAX_LED_TRIGGERS; i++) { - flash_data.flash_current[i] = u32->flash_current[i]; - flash_data.flash_duration[i] = u32->flash_duration[i]; - } switch (cmd) { case VIDIOC_MSM_FLASH_CFG32: + flash_data.cfg_type = u32->cfg_type; + for (i = 0; i < MAX_LED_TRIGGERS; i++) { + flash_data.flash_current[i] = u32->flash_current[i]; + flash_data.flash_duration[i] = u32->flash_duration[i]; + } cmd = VIDIOC_MSM_FLASH_CFG; switch (flash_data.cfg_type) { case CFG_FLASH_OFF: diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c index b3e5dc7f9cb8..c5cdee1bf706 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c +++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_tz_i2c.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016, 2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -520,14 +520,16 @@ int32_t msm_camera_tz_i2c_power_up( msm_camera_tz_get_ta_handle(), sensor_id, &sensor_secure); - if (!rc && sensor_secure) + if (!rc && sensor_secure) { /* Sensor validated by TA*/ sensor_info[sensor_id].ready++; + msm_camera_tz_unlock(); + } else { + msm_camera_tz_unlock(); msm_camera_tz_unload_ta(); rc = -EFAULT; } - msm_camera_tz_unlock(); } } else rc = -EFAULT; diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c index e1143c356721..fcef05374098 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -584,7 +584,12 @@ static int msm_sensor_config32(struct msm_sensor_ctrl_t *s_ctrl, pr_err("%s:%d: i2c_read failed\n", __func__, __LINE__); break; } - read_config_ptr->data = local_data; + if (copy_to_user(&read_config_ptr->data, + &local_data, sizeof(local_data))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } break; } case CFG_SLAVE_WRITE_I2C_ARRAY: { @@ -1098,7 +1103,12 @@ int msm_sensor_config(struct msm_sensor_ctrl_t *s_ctrl, void __user *argp) pr_err("%s:%d: i2c_read failed\n", __func__, __LINE__); break; } - read_config_ptr->data = local_data; + if (copy_to_user(&read_config_ptr->data, + &local_data, sizeof(local_data))) { + pr_err("%s:%d failed\n", __func__, __LINE__); + rc = -EFAULT; + break; + } break; } case CFG_SLAVE_WRITE_I2C_ARRAY: { diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c index 891e528f75f1..a41d7dba490e 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c +++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_driver.c @@ -469,17 +469,11 @@ static int32_t msm_sensor_create_pd_settings(void *setting, #ifdef CONFIG_COMPAT if (is_compat_task()) { - int i = 0; - struct msm_sensor_power_setting32 *power_setting_iter = - (struct msm_sensor_power_setting32 *)compat_ptr(( - (struct msm_camera_sensor_slave_info32 *)setting)-> - power_setting_array.power_setting); - - for (i = 0; i < size_down; i++) { - pd[i].config_val = power_setting_iter[i].config_val; - pd[i].delay = power_setting_iter[i].delay; - pd[i].seq_type = power_setting_iter[i].seq_type; - pd[i].seq_val = power_setting_iter[i].seq_val; + rc = msm_sensor_get_pw_settings_compat( + pd, pu, size_down); + if (rc < 0) { + pr_err("failed"); + return -EFAULT; } } else #endif diff --git a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c index c94ee509631f..bfb15846e73c 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c +++ b/drivers/media/platform/msm/camera_v2/sensor/ois/msm_ois.c @@ -774,11 +774,10 @@ static long msm_ois_subdev_do_ioctl( u32 = (struct msm_ois_cfg_data32 *)arg; parg = arg; - ois_data.cfgtype = u32->cfgtype; - switch (cmd) { case VIDIOC_MSM_OIS_CFG32: cmd = VIDIOC_MSM_OIS_CFG; + ois_data.cfgtype = u32->cfgtype; switch (u32->cfgtype) { case CFG_OIS_CONTROL: @@ -812,7 +811,6 @@ static long msm_ois_subdev_do_ioctl( settings.reg_setting = compat_ptr(settings32.reg_setting); - ois_data.cfgtype = u32->cfgtype; ois_data.cfg.settings = &settings; parg = &ois_data; break; diff --git a/drivers/media/platform/msm/sde/Kconfig b/drivers/media/platform/msm/sde/Kconfig index 85f5f4257ddb..d2c2e90f9de9 100644 --- a/drivers/media/platform/msm/sde/Kconfig +++ b/drivers/media/platform/msm/sde/Kconfig @@ -15,3 +15,15 @@ config MSM_SDE_ROTATOR_EVTLOG_DEBUG features to: Dump rotator registers during driver errors, panic driver during fatal errors and enable some rotator driver logging into an internal buffer (this avoids logging overhead). + +config MSM_SDE_HDMI_CEC + bool "QTI SDE HDMI CEC Driver" + depends on DRM_SDE_HDMI + select MEDIA_CEC_SUPPORT + select MEDIA_CEC_EDID + select MEDIA_CEC_NOTIFIER + ---help--- + The HDMI CEC driver provides support to enable HDMI CEC features + which allows various audiovisual products to communicate using HDMI + CEC links. CEC is a protocol defined in HDMI spec which consists of + both low-level and high-level protocol definition. diff --git a/drivers/media/platform/msm/sde/Makefile b/drivers/media/platform/msm/sde/Makefile index fe55d5044c3b..f39000a15993 100644 --- a/drivers/media/platform/msm/sde/Makefile +++ b/drivers/media/platform/msm/sde/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_MSM_SDE_ROTATOR) += rotator/ +obj-$(CONFIG_MSM_SDE_HDMI_CEC) += cec/ diff --git a/drivers/media/platform/msm/sde/cec/Makefile b/drivers/media/platform/msm/sde/cec/Makefile new file mode 100644 index 000000000000..2a9c30980512 --- /dev/null +++ b/drivers/media/platform/msm/sde/cec/Makefile @@ -0,0 +1,3 @@ +obj-y := \ + sde_hdmi_cec.o \ + sde_hdmi_cec_util.o diff --git a/drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c new file mode 100644 index 000000000000..b3798e8a9d24 --- /dev/null +++ b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec.c @@ -0,0 +1,429 @@ +/* Copyright (c) 2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/cec.h> +#include <media/cec.h> +#include <media/cec-edid.h> +#include <media/cec-notifier.h> + +#include "sde_hdmi_cec_util.h" + +#define CEC_NAME "sde-hdmi-cec" + +/* CEC Register Definition */ +#define CEC_INTR_MASK (BIT(1) | BIT(3) | BIT(7)) +#define CEC_SUPPORTED_HW_VERSION 0x30000001 + +#define HDMI_CEC_WR_RANGE (0x000002DC) +#define HDMI_CEC_RD_RANGE (0x000002E0) +#define HDMI_VERSION (0x000002E4) +#define HDMI_CEC_CTRL (0x0000028C) +#define HDMI_CEC_WR_DATA (0x00000290) +#define HDMI_CEC_RETRANSMIT (0x00000294) +#define HDMI_CEC_STATUS (0x00000298) +#define HDMI_CEC_INT (0x0000029C) +#define HDMI_CEC_ADDR (0x000002A0) +#define HDMI_CEC_TIME (0x000002A4) +#define HDMI_CEC_REFTIMER (0x000002A8) +#define HDMI_CEC_RD_DATA (0x000002AC) +#define HDMI_CEC_RD_FILTER (0x000002B0) +#define HDMI_CEC_COMPL_CTL (0x00000360) +#define HDMI_CEC_RD_START_RANGE (0x00000364) +#define HDMI_CEC_RD_TOTAL_RANGE (0x00000368) +#define HDMI_CEC_RD_ERR_RESP_LO (0x0000036C) +#define HDMI_CEC_WR_CHECK_CONFIG (0x00000370) + +enum cec_irq_status { + CEC_IRQ_FRAME_WR_DONE = 1 << 0, + CEC_IRQ_FRAME_RD_DONE = 1 << 1, + CEC_IRQ_FRAME_ERROR = 1 << 2, +}; + +struct sde_hdmi_cec { + struct cec_adapter *adap; + struct device *dev; + struct cec_hw_resource hw_res; + int irq; + enum cec_irq_status irq_status; + struct cec_notifier *notifier; +}; + +static int sde_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + struct sde_hdmi_cec *cec = adap->priv; + struct cec_hw_resource *hw = &cec->hw_res; + u32 hdmi_hw_version, reg_val; + + pr_debug("adap enable %d\n", enable); + + if (enable) { + pm_runtime_get_sync(cec->dev); + + /* 19.2Mhz * 0.00005 us = 950 = 0x3B6 */ + CEC_REG_WRITE(hw, HDMI_CEC_REFTIMER, (0x3B6 & 0xFFF) | BIT(16)); + + hdmi_hw_version = CEC_REG_READ(hw, HDMI_VERSION); + if (hdmi_hw_version >= CEC_SUPPORTED_HW_VERSION) { + CEC_REG_WRITE(hw, HDMI_CEC_RD_RANGE, 0x30AB9888); + CEC_REG_WRITE(hw, HDMI_CEC_WR_RANGE, 0x888AA888); + + CEC_REG_WRITE(hw, HDMI_CEC_RD_START_RANGE, 0x88888888); + CEC_REG_WRITE(hw, HDMI_CEC_RD_TOTAL_RANGE, 0x99); + CEC_REG_WRITE(hw, HDMI_CEC_COMPL_CTL, 0xF); + CEC_REG_WRITE(hw, HDMI_CEC_WR_CHECK_CONFIG, 0x4); + } else { + pr_err("CEC version %d is not supported.\n", + hdmi_hw_version); + return -EPERM; + } + + CEC_REG_WRITE(hw, HDMI_CEC_RD_FILTER, BIT(0) | (0x7FF << 4)); + CEC_REG_WRITE(hw, HDMI_CEC_TIME, BIT(0) | ((7 * 0x30) << 7)); + + /* Enable CEC interrupts */ + CEC_REG_WRITE(hw, HDMI_CEC_INT, CEC_INTR_MASK); + + /* Enable Engine */ + CEC_REG_WRITE(hw, HDMI_CEC_CTRL, BIT(0)); + } else { + /* Disable Engine */ + CEC_REG_WRITE(hw, HDMI_CEC_CTRL, 0); + + /* Disable CEC interrupts */ + reg_val = CEC_REG_READ(hw, HDMI_CEC_INT); + CEC_REG_WRITE(hw, HDMI_CEC_INT, reg_val & ~CEC_INTR_MASK); + + pm_runtime_put(cec->dev); + } + + return 0; +} + +static int sde_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr) +{ + struct sde_hdmi_cec *cec = adap->priv; + struct cec_hw_resource *hw = &cec->hw_res; + + pr_debug("set log addr %d\n", logical_addr); + + CEC_REG_WRITE(hw, HDMI_CEC_ADDR, logical_addr & 0xF); + + return 0; +} + +static int sde_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time, struct cec_msg *msg) +{ + struct sde_hdmi_cec *cec = adap->priv; + struct cec_hw_resource *hw = &cec->hw_res; + u32 frame_type; + int i; + u32 line_check_retry = 10; + + pr_debug("transmit msg [%d]->[%d]: len = %d, attampts=%d, signal_free_time=%d\n", + cec_msg_initiator(msg), cec_msg_destination(msg), msg->len, + attempts, signal_free_time); + + /* toggle cec in order to flush out bad hw state, if any */ + CEC_REG_WRITE(hw, HDMI_CEC_CTRL, 0); + CEC_REG_WRITE(hw, HDMI_CEC_CTRL, BIT(0)); + + /* make sure state is cleared */ + wmb(); + + CEC_REG_WRITE(hw, HDMI_CEC_RETRANSMIT, + ((attempts & 0xF) << 4) | BIT(0)); + + frame_type = cec_msg_is_broadcast(msg) ? BIT(0) : 0; + + /* header block */ + CEC_REG_WRITE(hw, HDMI_CEC_WR_DATA, + (((cec_msg_initiator(msg) << 4) | + cec_msg_destination(msg)) << 8) | frame_type); + + /* data block 0 : opcode */ + CEC_REG_WRITE(hw, HDMI_CEC_WR_DATA, + ((msg->len < 2 ? 0 : cec_msg_opcode(msg)) << 8) | frame_type); + + /* data block 1-14 : operand 0-13 */ + for (i = 2; i < msg->len; i++) + CEC_REG_WRITE(hw, HDMI_CEC_WR_DATA, + (msg->msg[i] << 8) | frame_type); + + /* check line status */ + while ((CEC_REG_READ(hw, HDMI_CEC_STATUS) & BIT(0)) && + line_check_retry) { + line_check_retry--; + pr_debug("CEC line is busy(%d)\n", line_check_retry); + schedule(); + } + + if (!line_check_retry && (CEC_REG_READ(hw, HDMI_CEC_STATUS) & BIT(0))) { + pr_err("CEC line is busy. Retry failed\n"); + return -EBUSY; + } + + /* start transmission */ + CEC_REG_WRITE(hw, HDMI_CEC_CTRL, BIT(0) | BIT(1) | + ((msg->len & 0x1F) << 4) | BIT(9)); + + return 0; +} + +static void sde_hdmi_cec_handle_rx_done(struct sde_hdmi_cec *cec) +{ + struct cec_hw_resource *hw = &cec->hw_res; + struct cec_msg msg = {}; + u32 data; + int i; + + pr_debug("rx done\n"); + + data = CEC_REG_READ(hw, HDMI_CEC_RD_DATA); + msg.len = (data & 0x1F00) >> 8; + if (msg.len < 1 || msg.len > CEC_MAX_MSG_SIZE) { + pr_err("invalid message size %d", msg.len); + return; + } + msg.msg[0] = data & 0xFF; + + for (i = 1; i < msg.len; i++) + msg.msg[i] = CEC_REG_READ(hw, HDMI_CEC_RD_DATA) & 0xFF; + + cec_received_msg(cec->adap, &msg); +} + +static void sde_hdmi_cec_handle_tx_done(struct sde_hdmi_cec *cec) +{ + pr_debug("tx done\n"); + cec_transmit_done(cec->adap, CEC_TX_STATUS_OK, 0, 0, 0, 0); +} + +static void sde_hdmi_cec_handle_tx_error(struct sde_hdmi_cec *cec) +{ + struct cec_hw_resource *hw = &cec->hw_res; + u32 cec_status = CEC_REG_READ(hw, HDMI_CEC_STATUS); + + pr_debug("tx error status %x\n", cec_status); + + if ((cec_status & 0xF0) == 0x10) + cec_transmit_done(cec->adap, + CEC_TX_STATUS_NACK, 0, 1, 0, 0); + else if ((cec_status & 0xF0) == 0x30) + cec_transmit_done(cec->adap, + CEC_TX_STATUS_ARB_LOST, 1, 0, 0, 0); + else + cec_transmit_done(cec->adap, + CEC_TX_STATUS_ERROR | CEC_TX_STATUS_MAX_RETRIES, + 0, 0, 0, 1); +} + +static irqreturn_t sde_hdmi_cec_irq_handler_thread(int irq, void *priv) +{ + struct sde_hdmi_cec *cec = priv; + + pr_debug("irq thread: status %x\n", cec->irq_status); + + if (cec->irq_status & CEC_IRQ_FRAME_WR_DONE) + sde_hdmi_cec_handle_tx_done(cec); + + if (cec->irq_status & CEC_IRQ_FRAME_ERROR) + sde_hdmi_cec_handle_tx_error(cec); + + if (cec->irq_status & CEC_IRQ_FRAME_RD_DONE) + sde_hdmi_cec_handle_rx_done(cec); + + cec->irq_status = 0; + + return IRQ_HANDLED; +} + +static irqreturn_t sde_hdmi_cec_irq_handler(int irq, void *priv) +{ + struct sde_hdmi_cec *cec = priv; + struct cec_hw_resource *hw = &cec->hw_res; + u32 data = CEC_REG_READ(hw, HDMI_CEC_INT); + + CEC_REG_WRITE(hw, HDMI_CEC_INT, data); + + pr_debug("irq handler: %x\n", data); + + if ((data & BIT(0)) && (data & BIT(1))) + cec->irq_status |= CEC_IRQ_FRAME_WR_DONE; + + if ((data & BIT(2)) && (data & BIT(3))) + cec->irq_status |= CEC_IRQ_FRAME_ERROR; + + if ((data & BIT(6)) && (data & BIT(7))) + cec->irq_status |= CEC_IRQ_FRAME_RD_DONE; + + return cec->irq_status ? IRQ_WAKE_THREAD : IRQ_HANDLED; +} + +static const struct cec_adap_ops sde_hdmi_cec_adap_ops = { + .adap_enable = sde_hdmi_cec_adap_enable, + .adap_log_addr = sde_hdmi_cec_adap_log_addr, + .adap_transmit = sde_hdmi_cec_adap_transmit, +}; + +static int sde_hdmi_cec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sde_hdmi_cec *cec; + struct device_node *np; + struct platform_device *hdmi_dev; + int ret; + + cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL); + if (!cec) + return -ENOMEM; + + cec->dev = dev; + + np = of_parse_phandle(pdev->dev.of_node, "qcom,hdmi-dev", 0); + if (!np) { + pr_err("failed to find hdmi node in device tree\n"); + return -ENODEV; + } + hdmi_dev = of_find_device_by_node(np); + if (hdmi_dev == NULL) + return -EPROBE_DEFER; + + cec->irq = of_irq_get(dev->of_node, 0); + if (cec->irq < 0) { + pr_err("failed to get irq\n"); + return cec->irq; + } + + ret = devm_request_threaded_irq(dev, cec->irq, sde_hdmi_cec_irq_handler, + sde_hdmi_cec_irq_handler_thread, 0, + pdev->name, cec); + if (ret) + return ret; + + ret = sde_hdmi_cec_init_resource(pdev, &cec->hw_res); + if (ret) + return ret; + + cec->adap = cec_allocate_adapter(&sde_hdmi_cec_adap_ops, cec, + CEC_NAME, + CEC_CAP_LOG_ADDRS | CEC_CAP_PASSTHROUGH | + CEC_CAP_TRANSMIT, 1); + ret = PTR_ERR_OR_ZERO(cec->adap); + if (ret) + return ret; + + ret = cec_register_adapter(cec->adap, &pdev->dev); + if (ret) + goto err_del_adap; + + cec->notifier = cec_notifier_get(&hdmi_dev->dev); + if (!cec->notifier) { + pr_err("failed to get cec notifier\n"); + goto err_del_adap; + } + + platform_set_drvdata(pdev, cec); + + pm_runtime_enable(dev); + + /* + * cec_register_cec_notifier has to be later than pm_runtime_enable + * because it calls adap_enable. + */ + cec_register_cec_notifier(cec->adap, cec->notifier); + + pr_debug("probe done\n"); + + return ret; + +err_del_adap: + cec_delete_adapter(cec->adap); + return ret; +} + +static int sde_hdmi_cec_remove(struct platform_device *pdev) +{ + struct sde_hdmi_cec *cec = platform_get_drvdata(pdev); + + pm_runtime_disable(&pdev->dev); + + cec_unregister_adapter(cec->adap); + cec_notifier_put(cec->notifier); + + devm_free_irq(&pdev->dev, cec->irq, cec); + sde_hdmi_cec_deinit_resource(pdev, &cec->hw_res); + + pr_debug("remove done\n"); + + return 0; +} + +static int __maybe_unused sde_hdmi_cec_runtime_suspend(struct device *dev) +{ + struct sde_hdmi_cec *cec = dev_get_drvdata(dev); + struct cec_hw_resource *hw = &cec->hw_res; + + pr_debug("runtime suspend\n"); + + return sde_hdmi_cec_enable_power(hw, false); +} + +static int __maybe_unused sde_hdmi_cec_runtime_resume(struct device *dev) +{ + struct sde_hdmi_cec *cec = dev_get_drvdata(dev); + struct cec_hw_resource *hw = &cec->hw_res; + + pr_debug("runtime resume\n"); + + return sde_hdmi_cec_enable_power(hw, true); +} + +static const struct dev_pm_ops sde_hdmi_cec_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(sde_hdmi_cec_runtime_suspend, + sde_hdmi_cec_runtime_resume, NULL) +}; + +static const struct of_device_id sde_hdmi_cec_match[] = { + { + .compatible = "qcom,hdmi-cec", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, sde_hdmi_cec_match); + +static struct platform_driver sde_hdmi_cec_pdrv = { + .probe = sde_hdmi_cec_probe, + .remove = sde_hdmi_cec_remove, + .driver = { + .name = CEC_NAME, + .of_match_table = sde_hdmi_cec_match, + .pm = &sde_hdmi_cec_pm_ops, + }, +}; + +module_platform_driver(sde_hdmi_cec_pdrv); +MODULE_DESCRIPTION("MSM SDE HDMI CEC driver"); diff --git a/drivers/media/platform/msm/sde/cec/sde_hdmi_cec_util.c b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec_util.c new file mode 100644 index 000000000000..323e0805c886 --- /dev/null +++ b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec_util.c @@ -0,0 +1,743 @@ +/* Copyright (c) 2012, 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> +#include <linux/delay.h> + +#include "sde_hdmi_cec_util.h" + +void sde_hdmi_cec_reg_w(struct cec_io_data *io, + u32 offset, u32 value, bool debug) +{ + u32 in_val; + + if (!io || !io->base) { + pr_err("invalid input\n"); + return; + } + + if (offset > io->len) { + pr_err("offset out of range\n"); + return; + } + + writel_relaxed(value, io->base + offset); + if (debug) { + in_val = readl_relaxed(io->base + offset); + pr_debug("[%08x] => %08x [%08x]\n", + (u32)(unsigned long)(io->base + offset), + value, in_val); + } +} + +u32 sde_hdmi_cec_reg_r(struct cec_io_data *io, u32 offset, bool debug) +{ + u32 value; + + if (!io || !io->base) { + pr_err("invalid input\n"); + return -EINVAL; + } + + if (offset > io->len) { + pr_err("offset out of range\n"); + return -EINVAL; + } + + value = readl_relaxed(io->base + offset); + if (debug) + pr_debug("[%08x] <= %08x\n", + (u32)(unsigned long)(io->base + offset), value); + + return value; +} + +void sde_hdmi_cec_reg_dump(void __iomem *base, u32 length, const char *prefix, + bool debug) +{ + if (debug) + print_hex_dump(KERN_INFO, prefix, DUMP_PREFIX_OFFSET, 32, 4, + __io_virt(base), length, false); +} + +static int sde_hdmi_cec_config_vreg(struct device *dev, + struct cec_vreg *in_vreg, int num_vreg, bool config) +{ + int i = 0, rc = 0; + struct cec_vreg *curr_vreg = NULL; + enum cec_vreg_type type; + + if (!in_vreg || !num_vreg) + return rc; + + if (config) { + for (i = 0; i < num_vreg; i++) { + curr_vreg = &in_vreg[i]; + curr_vreg->vreg = regulator_get(dev, + curr_vreg->vreg_name); + rc = PTR_RET(curr_vreg->vreg); + if (rc) { + pr_err("%s get failed. rc=%d\n", + curr_vreg->vreg_name, rc); + curr_vreg->vreg = NULL; + goto vreg_get_fail; + } + type = (regulator_count_voltages(curr_vreg->vreg) > 0) + ? CEC_REG_LDO : CEC_REG_VS; + if (type == CEC_REG_LDO) { + rc = regulator_set_voltage( + curr_vreg->vreg, + curr_vreg->min_voltage, + curr_vreg->max_voltage); + if (rc < 0) { + pr_err("%s set vltg fail\n", + curr_vreg->vreg_name); + goto vreg_set_voltage_fail; + } + } + } + } else { + for (i = num_vreg-1; i >= 0; i--) { + curr_vreg = &in_vreg[i]; + if (curr_vreg->vreg) { + type = (regulator_count_voltages( + curr_vreg->vreg) > 0) + ? CEC_REG_LDO : CEC_REG_VS; + if (type == CEC_REG_LDO) { + regulator_set_voltage(curr_vreg->vreg, + 0, curr_vreg->max_voltage); + } + regulator_put(curr_vreg->vreg); + curr_vreg->vreg = NULL; + } + } + } + return 0; + +vreg_unconfig: +if (type == CEC_REG_LDO) + regulator_set_load(curr_vreg->vreg, 0); + +vreg_set_voltage_fail: + regulator_put(curr_vreg->vreg); + curr_vreg->vreg = NULL; + +vreg_get_fail: + for (i--; i >= 0; i--) { + curr_vreg = &in_vreg[i]; + type = (regulator_count_voltages(curr_vreg->vreg) > 0) + ? CEC_REG_LDO : CEC_REG_VS; + goto vreg_unconfig; + } + return rc; +} + +static int sde_hdmi_cec_enable_vreg(struct cec_hw_resource *hw, int enable) +{ + int i = 0, rc = 0; + bool need_sleep; + struct cec_vreg *in_vreg = hw->vreg_config; + int num_vreg = hw->num_vreg; + + if (enable) { + for (i = 0; i < num_vreg; i++) { + rc = PTR_RET(in_vreg[i].vreg); + if (rc) { + pr_err("%s regulator error. rc=%d\n", + in_vreg[i].vreg_name, rc); + goto vreg_set_opt_mode_fail; + } + need_sleep = !regulator_is_enabled(in_vreg[i].vreg); + if (in_vreg[i].pre_on_sleep && need_sleep) + usleep_range(in_vreg[i].pre_on_sleep * 1000, + in_vreg[i].pre_on_sleep * 1000); + rc = regulator_set_load(in_vreg[i].vreg, + in_vreg[i].enable_load); + if (rc < 0) { + pr_err("%s set opt m fail\n", + in_vreg[i].vreg_name); + goto vreg_set_opt_mode_fail; + } + rc = regulator_enable(in_vreg[i].vreg); + if (in_vreg[i].post_on_sleep && need_sleep) + usleep_range(in_vreg[i].post_on_sleep * 1000, + in_vreg[i].post_on_sleep * 1000); + if (rc < 0) { + pr_err("%s enable failed\n", + in_vreg[i].vreg_name); + goto disable_vreg; + } + } + } else { + for (i = num_vreg-1; i >= 0; i--) { + if (in_vreg[i].pre_off_sleep) + usleep_range(in_vreg[i].pre_off_sleep * 1000, + in_vreg[i].pre_off_sleep * 1000); + regulator_set_load(in_vreg[i].vreg, + in_vreg[i].disable_load); + regulator_disable(in_vreg[i].vreg); + if (in_vreg[i].post_off_sleep) + usleep_range(in_vreg[i].post_off_sleep * 1000, + in_vreg[i].post_off_sleep * 1000); + } + } + return rc; + +disable_vreg: + regulator_set_load(in_vreg[i].vreg, in_vreg[i].disable_load); + +vreg_set_opt_mode_fail: + for (i--; i >= 0; i--) { + if (in_vreg[i].pre_off_sleep) + usleep_range(in_vreg[i].pre_off_sleep * 1000, + in_vreg[i].pre_off_sleep * 1000); + regulator_set_load(in_vreg[i].vreg, + in_vreg[i].disable_load); + regulator_disable(in_vreg[i].vreg); + if (in_vreg[i].post_off_sleep) + usleep_range(in_vreg[i].post_off_sleep * 1000, + in_vreg[i].post_off_sleep * 1000); + } + + return rc; +} + +static void sde_hdmi_cec_put_clk(struct cec_clk *clk_arry, int num_clk) +{ + int i; + + for (i = num_clk - 1; i >= 0; i--) { + if (clk_arry[i].clk) + clk_put(clk_arry[i].clk); + clk_arry[i].clk = NULL; + } +} + +static int sde_hdmi_cec_get_clk(struct device *dev, + struct cec_clk *clk_arry, int num_clk) +{ + int i, rc = 0; + + for (i = 0; i < num_clk; i++) { + clk_arry[i].clk = clk_get(dev, clk_arry[i].clk_name); + rc = PTR_RET(clk_arry[i].clk); + if (rc) { + pr_err("'%s' get failed. rc=%d\n", + clk_arry[i].clk_name, rc); + goto error; + } + } + + return rc; + +error: + sde_hdmi_cec_put_clk(clk_arry, num_clk); + + return rc; +} + +static int sde_hdmi_cec_enable_clk(struct cec_hw_resource *hw, int enable) +{ + int i, rc = 0; + struct cec_clk *clk_arry = hw->clk_config; + int num_clk = hw->num_clk; + + if (enable) { + for (i = 0; i < num_clk; i++) { + pr_debug("enable %s\n", clk_arry[i].clk_name); + if (clk_arry[i].clk) { + rc = clk_prepare_enable(clk_arry[i].clk); + if (rc) + pr_err("%s enable fail. rc=%d\n", + clk_arry[i].clk_name, rc); + } else { + pr_err("%s is not available\n", + clk_arry[i].clk_name); + rc = -EPERM; + } + } + } else { + for (i = num_clk - 1; i >= 0; i--) { + pr_debug("disable %s\n", clk_arry[i].clk_name); + + if (clk_arry[i].clk) + clk_disable_unprepare(clk_arry[i].clk); + else + pr_err("%s is not available\n", + clk_arry[i].clk_name); + } + } + + return rc; +} + +static int sde_hdmi_cec_pinctrl_enable(struct cec_hw_resource *hw, + bool enable) +{ + struct pinctrl_state *pin_state = NULL; + int rc = 0; + + if (!hw) { + pr_err("invalid input param hw:%pK\n", hw); + return -EINVAL; + } + + pr_debug("set cec pinctrl state %d\n", enable); + + pin_state = enable ? hw->pin_res.state_active : hw->pin_res.state_sleep; + + if (!IS_ERR_OR_NULL(hw->pin_res.pinctrl)) + rc = pinctrl_select_state(hw->pin_res.pinctrl, + pin_state); + else + pr_err("pinstate not found\n"); + + return rc; +} + +static void sde_hdmi_cec_put_dt_clock(struct platform_device *pdev, + struct cec_hw_resource *hw) +{ + if (!pdev || !hw) { + pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw); + return; + } + + if (hw->clk_config) { + sde_hdmi_cec_put_clk(hw->clk_config, hw->num_clk); + devm_kfree(&pdev->dev, hw->clk_config); + hw->clk_config = NULL; + } + hw->num_clk = 0; + + pr_debug("put dt clock\n"); +} + +static int sde_hdmi_cec_get_dt_clock(struct platform_device *pdev, + struct cec_hw_resource *hw) +{ + int i = 0; + int num_clk = 0; + const char *clock_name; + int rc = 0; + + if (!pdev || !hw) { + pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw); + return -EINVAL; + } + + hw->num_clk = 0; + num_clk = of_property_count_strings(pdev->dev.of_node, "clock-names"); + if (num_clk <= 0) { + pr_debug("clocks are not defined\n"); + return 0; + } + + hw->num_clk = num_clk; + hw->clk_config = devm_kzalloc(&pdev->dev, + sizeof(struct cec_clk) * num_clk, GFP_KERNEL); + if (!hw->clk_config) { + hw->num_clk = 0; + return -ENOMEM; + } + + for (i = 0; i < num_clk; i++) { + of_property_read_string_index(pdev->dev.of_node, "clock-names", + i, &clock_name); + strlcpy(hw->clk_config[i].clk_name, clock_name, + sizeof(hw->clk_config[i].clk_name)); + } + + rc = sde_hdmi_cec_get_clk(&pdev->dev, hw->clk_config, hw->num_clk); + if (rc) { + sde_hdmi_cec_put_dt_clock(pdev, hw); + return rc; + } + + pr_debug("get dt clock\n"); + + return 0; +} + +static int sde_hdmi_cec_get_dt_supply(struct platform_device *pdev, + struct cec_hw_resource *hw) +{ + int i = 0, rc = 0; + u32 tmp = 0; + struct device_node *of_node = NULL, *supply_root_node = NULL; + struct device_node *supply_node = NULL; + + if (!pdev || !hw) { + pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw); + return -EINVAL; + } + + of_node = pdev->dev.of_node; + + hw->num_vreg = 0; + supply_root_node = of_get_child_by_name(of_node, + "qcom,platform-supply-entries"); + if (!supply_root_node) { + pr_debug("no supply entry present\n"); + return rc; + } + + hw->num_vreg = of_get_available_child_count(supply_root_node); + if (hw->num_vreg == 0) { + pr_debug("no vreg present\n"); + return rc; + } + + pr_debug("vreg found. count=%d\n", hw->num_vreg); + hw->vreg_config = devm_kzalloc(&pdev->dev, sizeof(struct cec_vreg) * + hw->num_vreg, GFP_KERNEL); + if (!hw->vreg_config) { + rc = -ENOMEM; + return rc; + } + + for_each_available_child_of_node(supply_root_node, supply_node) { + const char *st = NULL; + + rc = of_property_read_string(supply_node, + "qcom,supply-name", &st); + if (rc) { + pr_err("error reading name. rc=%d\n", rc); + goto error; + } + + strlcpy(hw->vreg_config[i].vreg_name, st, + sizeof(hw->vreg_config[i].vreg_name)); + + rc = of_property_read_u32(supply_node, + "qcom,supply-min-voltage", &tmp); + if (rc) { + pr_err("error reading min volt. rc=%d\n", rc); + goto error; + } + hw->vreg_config[i].min_voltage = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-max-voltage", &tmp); + if (rc) { + pr_err("error reading max volt. rc=%d\n", rc); + goto error; + } + hw->vreg_config[i].max_voltage = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-enable-load", &tmp); + if (rc) { + pr_err("error reading enable load. rc=%d\n", rc); + goto error; + } + hw->vreg_config[i].enable_load = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-disable-load", &tmp); + if (rc) { + pr_err("error reading disable load. rc=%d\n", rc); + goto error; + } + hw->vreg_config[i].disable_load = tmp; + + rc = of_property_read_u32(supply_node, + "qcom,supply-pre-on-sleep", &tmp); + if (rc) + pr_debug("no supply pre sleep value. rc=%d\n", rc); + + hw->vreg_config[i].pre_on_sleep = (!rc ? tmp : 0); + + rc = of_property_read_u32(supply_node, + "qcom,supply-pre-off-sleep", &tmp); + if (rc) + pr_debug("no supply pre sleep value. rc=%d\n", rc); + + hw->vreg_config[i].pre_off_sleep = (!rc ? tmp : 0); + + rc = of_property_read_u32(supply_node, + "qcom,supply-post-on-sleep", &tmp); + if (rc) + pr_debug("no supply post sleep value. rc=%d\n", rc); + + hw->vreg_config[i].post_on_sleep = (!rc ? tmp : 0); + + rc = of_property_read_u32(supply_node, + "qcom,supply-post-off-sleep", &tmp); + if (rc) + pr_debug("no supply post sleep value. rc=%d\n", rc); + + hw->vreg_config[i].post_off_sleep = (!rc ? tmp : 0); + + pr_debug("%s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n", + hw->vreg_config[i].vreg_name, + hw->vreg_config[i].min_voltage, + hw->vreg_config[i].max_voltage, + hw->vreg_config[i].enable_load, + hw->vreg_config[i].disable_load, + hw->vreg_config[i].pre_on_sleep, + hw->vreg_config[i].post_on_sleep, + hw->vreg_config[i].pre_off_sleep, + hw->vreg_config[i].post_off_sleep); + ++i; + + rc = 0; + } + + rc = sde_hdmi_cec_config_vreg(&pdev->dev, + hw->vreg_config, hw->num_vreg, true); + if (rc) + goto error; + + pr_debug("get dt supply\n"); + + return rc; + +error: + if (hw->vreg_config) { + devm_kfree(&pdev->dev, hw->vreg_config); + hw->vreg_config = NULL; + hw->num_vreg = 0; + } + + return rc; +} + +static void sde_hdmi_cec_put_dt_supply(struct platform_device *pdev, + struct cec_hw_resource *hw) +{ + if (!pdev || !hw) { + pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw); + return; + } + + sde_hdmi_cec_config_vreg(&pdev->dev, + hw->vreg_config, hw->num_vreg, false); + + if (hw->vreg_config) { + devm_kfree(&pdev->dev, hw->vreg_config); + hw->vreg_config = NULL; + } + hw->num_vreg = 0; + + pr_debug("put dt supply\n"); +} + +static int sde_hdmi_cec_get_dt_pinres(struct platform_device *pdev, + struct cec_hw_resource *hw) +{ + if (!pdev || !hw) { + pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw); + return -EINVAL; + } + + hw->pin_res.pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(hw->pin_res.pinctrl)) { + pr_err("failed to get pinctrl\n"); + return PTR_ERR(hw->pin_res.pinctrl); + } + + hw->pin_res.state_active = + pinctrl_lookup_state(hw->pin_res.pinctrl, "cec_active"); + if (IS_ERR_OR_NULL(hw->pin_res.state_active)) + pr_debug("cannot get active pinstate\n"); + + hw->pin_res.state_sleep = + pinctrl_lookup_state(hw->pin_res.pinctrl, "cec_sleep"); + if (IS_ERR_OR_NULL(hw->pin_res.state_sleep)) + pr_debug("cannot get sleep pinstate\n"); + + pr_debug("get dt pinres data\n"); + + return 0; +} + +static void sde_hdmi_cec_put_dt_pinres(struct platform_device *pdev, + struct cec_hw_resource *hw) +{ + if (!pdev || !hw) { + pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw); + return; + } + + if (!IS_ERR_OR_NULL(hw->pin_res.pinctrl)) + devm_pinctrl_put(hw->pin_res.pinctrl); +} + +static void sde_hdmi_cec_deinit_power(struct platform_device *pdev, + struct cec_hw_resource *hw) +{ + if (!pdev || !hw) { + pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw); + return; + } + + sde_hdmi_cec_put_dt_supply(pdev, hw); + sde_hdmi_cec_put_dt_clock(pdev, hw); + sde_hdmi_cec_put_dt_pinres(pdev, hw); + + pr_debug("put dt power data\n"); +} + +static int sde_hdmi_cec_init_power(struct platform_device *pdev, + struct cec_hw_resource *hw) +{ + int rc = 0; + + if (!pdev || !hw) { + pr_err("invalid input param pdev:%pK hw:%pK\n", pdev, hw); + return -EINVAL; + } + + /* VREG */ + rc = sde_hdmi_cec_get_dt_supply(pdev, hw); + if (rc) { + pr_err("get_dt_supply failed. rc=%d\n", rc); + goto error; + } + + /* Clock */ + rc = sde_hdmi_cec_get_dt_clock(pdev, hw); + if (rc) { + pr_err("get_dt_clock failed. rc=%d\n", rc); + goto error; + } + + /* Pinctrl */ + rc = sde_hdmi_cec_get_dt_pinres(pdev, hw); + if (rc) { + pr_err("get_dt_pinres failed. rc=%d\n", rc); + goto error; + } + + pr_debug("get dt power data\n"); + + return rc; + +error: + sde_hdmi_cec_deinit_power(pdev, hw); + return rc; +} + +static int sde_hdmi_cec_init_io(struct platform_device *pdev, + struct cec_hw_resource *hw) +{ + struct resource *res = NULL; + struct cec_io_data *io_data = NULL; + const char *reg_name; + + if (!pdev || !hw) { + pr_err("invalid input\n"); + return -EINVAL; + } + + if (of_property_read_string(pdev->dev.of_node, "reg-names", + ®_name)) { + pr_err("cec reg not defined\n"); + return -ENODEV; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, reg_name); + if (!res) { + pr_err("%s get_res_byname failed\n", reg_name); + return -ENODEV; + } + + io_data = &hw->io_res; + io_data->len = (u32)resource_size(res); + io_data->base = ioremap(res->start, io_data->len); + if (!io_data->base) { + pr_err("%s ioremap failed\n", reg_name); + return -EIO; + } + + return 0; +} + +static void sde_hdmi_cec_deinit_io(struct platform_device *pdev, + struct cec_hw_resource *hw) +{ + struct cec_io_data *io_data = NULL; + + if (!pdev || !hw) { + pr_err("invalid input\n"); + return; + } + + io_data = &hw->io_res; + + if (io_data->base) { + iounmap(io_data->base); + io_data->base = NULL; + } + io_data->len = 0; +} + +int sde_hdmi_cec_init_resource(struct platform_device *pdev, + struct cec_hw_resource *hw) +{ + int rc = 0; + + /* power */ + rc = sde_hdmi_cec_init_power(pdev, hw); + if (rc) + return rc; + + /* io */ + rc = sde_hdmi_cec_init_io(pdev, hw); + if (rc) + goto io_error; + + pr_debug("cec init resource\n"); + + return rc; + +io_error: + sde_hdmi_cec_deinit_power(pdev, hw); + return rc; +} + +void sde_hdmi_cec_deinit_resource(struct platform_device *pdev, + struct cec_hw_resource *hw) +{ + sde_hdmi_cec_deinit_power(pdev, hw); + sde_hdmi_cec_deinit_io(pdev, hw); + + pr_debug("cec deinit resource\n"); +} + +int sde_hdmi_cec_enable_power(struct cec_hw_resource *hw, bool enable) +{ + int rc = 0; + + rc = sde_hdmi_cec_enable_vreg(hw, enable); + if (rc) + return rc; + + rc = sde_hdmi_cec_pinctrl_enable(hw, enable); + if (rc) + return rc; + + rc = sde_hdmi_cec_enable_clk(hw, enable); + if (rc) + return rc; + + pr_debug("cec power enable = %d\n", enable); + + return rc; +} diff --git a/drivers/media/platform/msm/sde/cec/sde_hdmi_cec_util.h b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec_util.h new file mode 100644 index 000000000000..f92c43ea3288 --- /dev/null +++ b/drivers/media/platform/msm/sde/cec/sde_hdmi_cec_util.h @@ -0,0 +1,93 @@ +/* Copyright (c) 2012, 2015-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef __SDE_HDMI_CEC_UTIL_H__ +#define __SDE_HDMI_CEC_UTIL_H__ + +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/pinctrl/consumer.h> +#include <linux/types.h> + +#ifdef DEBUG +#define CEC_REG_WRITE(hw, off, val) \ + sde_hdmi_cec_reg_w(&(hw)->io_res, (off), (val), true) +#define CEC_REG_READ(hw, off) \ + sde_hdmi_cec_reg_r(&(hw)->io_res, (off), true) +#else +#define CEC_REG_WRITE(hw, off, val) \ + sde_hdmi_cec_reg_w(&(hw)->io_res, (off), (val), false) +#define CEC_REG_READ(hw, off) \ + sde_hdmi_cec_reg_r(&(hw)->io_res, (off), false) +#endif + +struct cec_io_data { + u32 len; + void __iomem *base; +}; + +enum cec_vreg_type { + CEC_REG_LDO, + CEC_REG_VS, +}; + +struct cec_vreg { + struct regulator *vreg; /* vreg handle */ + char vreg_name[32]; + int min_voltage; + int max_voltage; + int enable_load; + int disable_load; + int pre_on_sleep; + int post_on_sleep; + int pre_off_sleep; + int post_off_sleep; +}; + +struct cec_clk { + struct clk *clk; /* clk handle */ + char clk_name[32]; +}; + +struct cec_pin_res { + struct pinctrl *pinctrl; + struct pinctrl_state *state_active; + struct pinctrl_state *state_sleep; +}; + +struct cec_hw_resource { + /* power */ + unsigned num_vreg; + struct cec_vreg *vreg_config; + unsigned num_clk; + struct cec_clk *clk_config; + struct cec_pin_res pin_res; + + /* io */ + struct cec_io_data io_res; +}; + +void sde_hdmi_cec_reg_w(struct cec_io_data *io, + u32 offset, u32 value, bool debug); +u32 sde_hdmi_cec_reg_r(struct cec_io_data *io, u32 offset, bool debug); +void sde_hdmi_cec_reg_dump(void __iomem *base, u32 length, const char *prefix, + bool debug); + +int sde_hdmi_cec_init_resource(struct platform_device *pdev, + struct cec_hw_resource *hw); +void sde_hdmi_cec_deinit_resource(struct platform_device *pdev, + struct cec_hw_resource *hw); +int sde_hdmi_cec_enable_power(struct cec_hw_resource *hw, bool enable); + +#endif /* __SDE_HDMI_CEC_UTIL_H__ */ + diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c index 62980f345f60..abf20aef1256 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c @@ -621,9 +621,10 @@ static int sde_rotator_secure_session_ctrl(bool enable) if (mdata->wait_for_transition && mdata->secure_session_ctrl && mdata->callback_request) { ret = mdata->wait_for_transition(mdata->sec_cam_en, enable); - if (ret) { + if (ret < 0) { SDEROT_ERR("failed Secure wait for transition %d\n", ret); + ret = -EPERM; } else { if (mdata->sec_cam_en ^ enable) { mdata->sec_cam_en = enable; @@ -984,6 +985,7 @@ static int sde_rotator_init_queue(struct sde_rot_mgr *mgr) { int i, size, ret = 0; char name[32]; + struct sched_param param = { .sched_priority = 5 }; size = sizeof(struct sde_rot_queue) * mgr->queue_count; mgr->commitq = devm_kzalloc(mgr->device, size, GFP_KERNEL); @@ -994,11 +996,21 @@ static int sde_rotator_init_queue(struct sde_rot_mgr *mgr) snprintf(name, sizeof(name), "rot_commitq_%d_%d", mgr->device->id, i); SDEROT_DBG("work queue name=%s\n", name); - mgr->commitq[i].rot_work_queue = - alloc_ordered_workqueue("%s", - WQ_MEM_RECLAIM | WQ_HIGHPRI, name); - if (!mgr->commitq[i].rot_work_queue) { + init_kthread_worker(&mgr->commitq[i].rot_kw); + mgr->commitq[i].rot_thread = kthread_run(kthread_worker_fn, + &mgr->commitq[i].rot_kw, name); + if (IS_ERR(mgr->commitq[i].rot_thread)) { ret = -EPERM; + mgr->commitq[i].rot_thread = NULL; + break; + } + + ret = sched_setscheduler(mgr->commitq[i].rot_thread, + SCHED_FIFO, ¶m); + if (ret) { + SDEROT_ERR( + "failed to set kthread priority for commitq %d\n", + ret); break; } @@ -1015,10 +1027,21 @@ static int sde_rotator_init_queue(struct sde_rot_mgr *mgr) snprintf(name, sizeof(name), "rot_doneq_%d_%d", mgr->device->id, i); SDEROT_DBG("work queue name=%s\n", name); - mgr->doneq[i].rot_work_queue = alloc_ordered_workqueue("%s", - WQ_MEM_RECLAIM | WQ_HIGHPRI, name); - if (!mgr->doneq[i].rot_work_queue) { + init_kthread_worker(&mgr->doneq[i].rot_kw); + mgr->doneq[i].rot_thread = kthread_run(kthread_worker_fn, + &mgr->doneq[i].rot_kw, name); + if (IS_ERR(mgr->doneq[i].rot_thread)) { ret = -EPERM; + mgr->doneq[i].rot_thread = NULL; + break; + } + + ret = sched_setscheduler(mgr->doneq[i].rot_thread, + SCHED_FIFO, ¶m); + if (ret) { + SDEROT_ERR( + "failed to set kthread priority for doneq %d\n", + ret); break; } @@ -1034,18 +1057,20 @@ static void sde_rotator_deinit_queue(struct sde_rot_mgr *mgr) if (mgr->commitq) { for (i = 0; i < mgr->queue_count; i++) { - if (mgr->commitq[i].rot_work_queue) - destroy_workqueue( - mgr->commitq[i].rot_work_queue); + if (mgr->commitq[i].rot_thread) { + flush_kthread_worker(&mgr->commitq[i].rot_kw); + kthread_stop(mgr->commitq[i].rot_thread); + } } devm_kfree(mgr->device, mgr->commitq); mgr->commitq = NULL; } if (mgr->doneq) { for (i = 0; i < mgr->queue_count; i++) { - if (mgr->doneq[i].rot_work_queue) - destroy_workqueue( - mgr->doneq[i].rot_work_queue); + if (mgr->doneq[i].rot_thread) { + flush_kthread_worker(&mgr->doneq[i].rot_kw); + kthread_stop(mgr->doneq[i].rot_thread); + } } devm_kfree(mgr->device, mgr->doneq); mgr->doneq = NULL; @@ -1090,6 +1115,8 @@ static int sde_rotator_assign_queue(struct sde_rot_mgr *mgr, if (IS_ERR_OR_NULL(hw)) { SDEROT_ERR("fail to allocate hw\n"); ret = PTR_ERR(hw); + if (!ret) + ret = -EINVAL; } else { queue->hw = hw; } @@ -1166,7 +1193,7 @@ void sde_rotator_queue_request(struct sde_rot_mgr *mgr, if (entry->item.ts) entry->item.ts[SDE_ROTATOR_TS_QUEUE] = ktime_get(); - queue_work(queue->rot_work_queue, &entry->commit_work); + queue_kthread_work(&queue->rot_kw, &entry->commit_work); } } @@ -1377,12 +1404,13 @@ static void sde_rotator_release_entry(struct sde_rot_mgr *mgr, * * Note this asynchronous handler is protected by hal lock. */ -static void sde_rotator_commit_handler(struct work_struct *work) +static void sde_rotator_commit_handler(struct kthread_work *work) { struct sde_rot_entry *entry; struct sde_rot_entry_container *request; struct sde_rot_hw_resource *hw; struct sde_rot_mgr *mgr; + struct sched_param param = { .sched_priority = 5 }; int ret; entry = container_of(work, struct sde_rot_entry, commit_work); @@ -1393,6 +1421,12 @@ static void sde_rotator_commit_handler(struct work_struct *work) return; } + ret = sched_setscheduler(entry->fenceq->rot_thread, SCHED_FIFO, ¶m); + if (ret) { + SDEROT_WARN("Fail to set kthread priority for fenceq: %d\n", + ret); + } + mgr = entry->private->mgr; SDEROT_EVTLOG( @@ -1466,7 +1500,7 @@ static void sde_rotator_commit_handler(struct work_struct *work) if (entry->item.ts) entry->item.ts[SDE_ROTATOR_TS_FLUSH] = ktime_get(); - queue_work(entry->doneq->rot_work_queue, &entry->done_work); + queue_kthread_work(&entry->doneq->rot_kw, &entry->done_work); sde_rot_mgr_unlock(mgr); return; error: @@ -1478,8 +1512,8 @@ get_hw_res_err: sde_rotator_release_entry(mgr, entry); atomic_dec(&request->pending_count); atomic_inc(&request->failed_count); - if (request->retireq && request->retire_work) - queue_work(request->retireq, request->retire_work); + if (request->retire_kw && request->retire_work) + queue_kthread_work(request->retire_kw, request->retire_work); sde_rot_mgr_unlock(mgr); } @@ -1493,7 +1527,7 @@ get_hw_res_err: * * Note this asynchronous handler is protected by hal lock. */ -static void sde_rotator_done_handler(struct work_struct *work) +static void sde_rotator_done_handler(struct kthread_work *work) { struct sde_rot_entry *entry; struct sde_rot_entry_container *request; @@ -1551,8 +1585,8 @@ static void sde_rotator_done_handler(struct work_struct *work) ATRACE_INT("sde_rot_done", 1); sde_rotator_release_entry(mgr, entry); atomic_dec(&request->pending_count); - if (request->retireq && request->retire_work) - queue_work(request->retireq, request->retire_work); + if (request->retire_kw && request->retire_work) + queue_kthread_work(request->retire_kw, request->retire_work); if (entry->item.ts) entry->item.ts[SDE_ROTATOR_TS_RETIRE] = ktime_get(); sde_rot_mgr_unlock(mgr); @@ -1918,8 +1952,10 @@ static int sde_rotator_add_request(struct sde_rot_mgr *mgr, entry->request = req; - INIT_WORK(&entry->commit_work, sde_rotator_commit_handler); - INIT_WORK(&entry->done_work, sde_rotator_done_handler); + init_kthread_work(&entry->commit_work, + sde_rotator_commit_handler); + init_kthread_work(&entry->done_work, + sde_rotator_done_handler); SDEROT_DBG("Entry added. wbidx=%u, src{%u,%u,%u,%u}f=%u\n" "dst{%u,%u,%u,%u}f=%u session_id=%u\n", item->wb_idx, item->src_rect.x, item->src_rect.y, @@ -1957,24 +1993,26 @@ static void sde_rotator_cancel_request(struct sde_rot_mgr *mgr, struct sde_rot_entry *entry; int i; - /* - * To avoid signal the rotation entry output fence in the wrong - * order, all the entries in the same request needs to be canceled - * first, before signaling the output fence. - */ - SDEROT_DBG("cancel work start\n"); - sde_rot_mgr_unlock(mgr); - for (i = req->count - 1; i >= 0; i--) { - entry = req->entries + i; - cancel_work_sync(&entry->commit_work); - cancel_work_sync(&entry->done_work); - } - sde_rot_mgr_lock(mgr); - SDEROT_DBG("cancel work done\n"); - for (i = req->count - 1; i >= 0; i--) { - entry = req->entries + i; - sde_rotator_signal_output(entry); - sde_rotator_release_entry(mgr, entry); + if (atomic_read(&req->pending_count)) { + /* + * To avoid signal the rotation entry output fence in the wrong + * order, all the entries in the same request needs to be + * canceled first, before signaling the output fence. + */ + SDEROT_DBG("cancel work start\n"); + sde_rot_mgr_unlock(mgr); + for (i = req->count - 1; i >= 0; i--) { + entry = req->entries + i; + flush_kthread_worker(&entry->commitq->rot_kw); + flush_kthread_worker(&entry->doneq->rot_kw); + } + sde_rot_mgr_lock(mgr); + SDEROT_DBG("cancel work done\n"); + for (i = req->count - 1; i >= 0; i--) { + entry = req->entries + i; + sde_rotator_signal_output(entry); + sde_rotator_release_entry(mgr, entry); + } } list_del_init(&req->list); @@ -1999,7 +2037,7 @@ static void sde_rotator_free_completed_request(struct sde_rot_mgr *mgr, list_for_each_entry_safe(req, req_next, &private->req_list, list) { if ((atomic_read(&req->pending_count) == 0) && - (!req->retire_work && !req->retireq)) { + (!req->retire_work && !req->retire_kw)) { list_del_init(&req->list); devm_kfree(&mgr->pdev->dev, req); } diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h index 2073c6d9f115..41918dd9b43e 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h @@ -21,6 +21,7 @@ #include <linux/types.h> #include <linux/cdev.h> #include <linux/pm_runtime.h> +#include <linux/kthread.h> #include "sde_rotator_base.h" #include "sde_rotator_util.h" @@ -184,7 +185,8 @@ struct sde_rot_hw_resource { }; struct sde_rot_queue { - struct workqueue_struct *rot_work_queue; + struct kthread_worker rot_kw; + struct task_struct *rot_thread; struct sde_rot_timeline *timeline; struct sde_rot_hw_resource *hw; }; @@ -195,8 +197,8 @@ struct sde_rot_entry_container { u32 count; atomic_t pending_count; atomic_t failed_count; - struct workqueue_struct *retireq; - struct work_struct *retire_work; + struct kthread_worker *retire_kw; + struct kthread_work *retire_work; struct sde_rot_entry *entries; }; @@ -205,8 +207,8 @@ struct sde_rot_file_private; struct sde_rot_entry { struct sde_rotation_item item; - struct work_struct commit_work; - struct work_struct done_work; + struct kthread_work commit_work; + struct kthread_work done_work; struct sde_rot_queue *commitq; struct sde_rot_queue *fenceq; struct sde_rot_queue *doneq; diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c index f41382b5b20c..1966fa9805c0 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c @@ -1023,6 +1023,9 @@ static ssize_t sde_rotator_debug_base_offset_write(struct file *file, if (sscanf(buf, "%5x %x", &off, &cnt) < 2) return -EINVAL; + if (off % sizeof(u32)) + return -EINVAL; + if (off > dbg->max_offset) return -EINVAL; @@ -1091,6 +1094,9 @@ static ssize_t sde_rotator_debug_base_reg_write(struct file *file, if (cnt < 2) return -EFAULT; + if (off % sizeof(u32)) + return -EFAULT; + if (off >= dbg->max_offset) return -EFAULT; @@ -1139,6 +1145,9 @@ static ssize_t sde_rotator_debug_base_reg_read(struct file *file, goto debug_read_error; } + if (dbg->off % sizeof(u32)) + return -EFAULT; + ptr = dbg->base + dbg->off; tot = 0; diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c index cfee4efb6f16..08bbed147c86 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c @@ -53,8 +53,8 @@ #define SDE_ROTATOR_DEGREE_180 180 #define SDE_ROTATOR_DEGREE_90 90 -static void sde_rotator_submit_handler(struct work_struct *work); -static void sde_rotator_retire_handler(struct work_struct *work); +static void sde_rotator_submit_handler(struct kthread_work *work); +static void sde_rotator_retire_handler(struct kthread_work *work); #ifdef CONFIG_COMPAT static long sde_rotator_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg); @@ -466,8 +466,8 @@ static void sde_rotator_stop_streaming(struct vb2_queue *q) sde_rotator_cancel_all_requests(rot_dev->mgr, ctx->private); sde_rot_mgr_unlock(rot_dev->mgr); mutex_unlock(q->lock); - cancel_work_sync(&ctx->submit_work); - cancel_work_sync(&ctx->retire_work); + flush_kthread_work(&ctx->submit_work); + flush_kthread_work(&ctx->retire_work); mutex_lock(q->lock); } @@ -480,7 +480,7 @@ static void sde_rotator_stop_streaming(struct vb2_queue *q) struct sde_rotator_vbinfo *vbinfo = &ctx->vbinfo_cap[i]; - if (vbinfo->fence && vbinfo->fd < 0) { + if (vbinfo->fence) { /* fence is not used */ SDEDEV_DBG(rot_dev->dev, "put fence s:%d t:%d i:%d\n", @@ -765,8 +765,6 @@ static ssize_t sde_rotator_ctx_show(struct kobject *kobj, ctx->format_cap.fmt.pix.sizeimage); SPRINT("abort_pending=%d\n", ctx->abort_pending); SPRINT("command_pending=%d\n", atomic_read(&ctx->command_pending)); - SPRINT("submit_work=%d\n", work_busy(&ctx->submit_work)); - SPRINT("retire_work=%d\n", work_busy(&ctx->retire_work)); SPRINT("sequence=%u\n", sde_rotator_get_timeline_commit_ts(ctx->work_queue.timeline)); SPRINT("timestamp=%u\n", @@ -923,8 +921,8 @@ static int sde_rotator_open(struct file *file) ctx->crop_out.width = 640; ctx->crop_out.height = 480; init_waitqueue_head(&ctx->wait_queue); - INIT_WORK(&ctx->submit_work, sde_rotator_submit_handler); - INIT_WORK(&ctx->retire_work, sde_rotator_retire_handler); + init_kthread_work(&ctx->submit_work, sde_rotator_submit_handler); + init_kthread_work(&ctx->retire_work, sde_rotator_retire_handler); v4l2_fh_init(&ctx->fh, video); file->private_data = &ctx->fh; @@ -954,14 +952,16 @@ static int sde_rotator_open(struct file *file) snprintf(name, sizeof(name), "rot_fenceq_%d_%d", rot_dev->dev->id, ctx->session_id); - ctx->work_queue.rot_work_queue = alloc_ordered_workqueue("%s", - WQ_MEM_RECLAIM | WQ_HIGHPRI, name); - if (!ctx->work_queue.rot_work_queue) { - SDEDEV_ERR(ctx->rot_dev->dev, "fail allocate workqueue\n"); + init_kthread_worker(&ctx->work_queue.rot_kw); + ctx->work_queue.rot_thread = kthread_run(kthread_worker_fn, + &ctx->work_queue.rot_kw, name); + if (IS_ERR(ctx->work_queue.rot_thread)) { + SDEDEV_ERR(ctx->rot_dev->dev, "fail allocate kthread\n"); ret = -EPERM; + ctx->work_queue.rot_thread = NULL; goto error_alloc_workqueue; } - SDEDEV_DBG(ctx->rot_dev->dev, "work queue name=%s\n", name); + SDEDEV_DBG(ctx->rot_dev->dev, "kthread name=%s\n", name); snprintf(name, sizeof(name), "%d_%d", rot_dev->dev->id, ctx->session_id); @@ -1010,7 +1010,8 @@ error_ctrl_handler: error_open_session: sde_rot_mgr_unlock(rot_dev->mgr); sde_rotator_destroy_timeline(ctx->work_queue.timeline); - destroy_workqueue(ctx->work_queue.rot_work_queue); + flush_kthread_worker(&ctx->work_queue.rot_kw); + kthread_stop(ctx->work_queue.rot_thread); error_alloc_workqueue: sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group); error_create_sysfs: @@ -1045,20 +1046,17 @@ static int sde_rotator_release(struct file *file) v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE); mutex_unlock(&rot_dev->lock); - SDEDEV_DBG(rot_dev->dev, "release submit work s:%d w:%x\n", - session_id, work_busy(&ctx->submit_work)); - cancel_work_sync(&ctx->submit_work); + SDEDEV_DBG(rot_dev->dev, "release submit work s:%d\n", session_id); + flush_kthread_worker(&ctx->work_queue.rot_kw); SDEDEV_DBG(rot_dev->dev, "release session s:%d\n", session_id); sde_rot_mgr_lock(rot_dev->mgr); sde_rotator_session_close(rot_dev->mgr, ctx->private, session_id); sde_rot_mgr_unlock(rot_dev->mgr); - SDEDEV_DBG(rot_dev->dev, "release retire work s:%d w:%x\n", - session_id, work_busy(&ctx->retire_work)); - cancel_work_sync(&ctx->retire_work); + SDEDEV_DBG(rot_dev->dev, "release retire work s:%d\n", session_id); mutex_lock(&rot_dev->lock); SDEDEV_DBG(rot_dev->dev, "release context s:%d\n", session_id); sde_rotator_destroy_timeline(ctx->work_queue.timeline); - destroy_workqueue(ctx->work_queue.rot_work_queue); + kthread_stop(ctx->work_queue.rot_thread); sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group); kobject_put(&ctx->kobj); v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); @@ -1459,7 +1457,7 @@ static int sde_rotator_dqbuf(struct file *file, && (buf->index < ctx->nbuf_cap)) { int idx = buf->index; - if (ctx->vbinfo_cap[idx].fence && ctx->vbinfo_cap[idx].fd < 0) { + if (ctx->vbinfo_cap[idx].fence) { /* fence is not used */ SDEDEV_DBG(ctx->rot_dev->dev, "put fence s:%d i:%d\n", ctx->session_id, idx); @@ -1787,6 +1785,7 @@ static long sde_rotator_private_ioctl(struct file *file, void *fh, struct msm_sde_rotator_fence *fence = arg; struct msm_sde_rotator_comp_ratio *comp_ratio = arg; struct sde_rotator_vbinfo *vbinfo; + int ret; switch (cmd) { case VIDIOC_S_SDE_ROTATOR_FENCE: @@ -1845,18 +1844,39 @@ static long sde_rotator_private_ioctl(struct file *file, void *fh, vbinfo = &ctx->vbinfo_cap[fence->index]; - if (vbinfo->fence == NULL) { - vbinfo->fd = -1; - } else { - vbinfo->fd = - sde_rotator_get_sync_fence_fd(vbinfo->fence); - if (vbinfo->fd < 0) { + if (!vbinfo) + return -EINVAL; + + if (vbinfo->fence) { + ret = sde_rotator_get_sync_fence_fd(vbinfo->fence); + if (ret < 0) { SDEDEV_ERR(rot_dev->dev, "fail get fence fd s:%d\n", ctx->session_id); - return vbinfo->fd; + return ret; } + + /* + * Loose any reference to sync fence once we pass + * it to user. Driver does not clean up user + * unclosed fence descriptors. + */ + vbinfo->fence = NULL; + + /* + * Cache fence descriptor in case user calls this + * ioctl multiple times. Cached value would be stale + * if user duplicated and closed old descriptor. + */ + vbinfo->fd = ret; + } else if (!sde_rotator_get_fd_sync_fence(vbinfo->fd)) { + /* + * User has closed cached fence descriptor. + * Invalidate descriptor cache. + */ + vbinfo->fd = -1; } + fence->fd = vbinfo->fd; SDEDEV_DBG(rot_dev->dev, @@ -2023,7 +2043,7 @@ static const struct v4l2_ioctl_ops sde_rotator_ioctl_ops = { * * This function is scheduled in work queue context. */ -static void sde_rotator_retire_handler(struct work_struct *work) +static void sde_rotator_retire_handler(struct kthread_work *work) { struct vb2_v4l2_buffer *src_buf; struct vb2_v4l2_buffer *dst_buf; @@ -2209,7 +2229,7 @@ static int sde_rotator_process_buffers(struct sde_rotator_ctx *ctx, goto error_init_request; } - req->retireq = ctx->work_queue.rot_work_queue; + req->retire_kw = &ctx->work_queue.rot_kw; req->retire_work = &ctx->retire_work; ret = sde_rotator_handle_request_common( @@ -2238,7 +2258,7 @@ error_null_buffer: * * This function is scheduled in work queue context. */ -static void sde_rotator_submit_handler(struct work_struct *work) +static void sde_rotator_submit_handler(struct kthread_work *work) { struct sde_rotator_ctx *ctx; struct sde_rotator_device *rot_dev; @@ -2325,7 +2345,7 @@ static void sde_rotator_device_run(void *priv) /* disconnect request (will be freed by core layer) */ sde_rot_mgr_lock(rot_dev->mgr); - ctx->request->retireq = NULL; + ctx->request->retire_kw = NULL; ctx->request->retire_work = NULL; ctx->request = NULL; sde_rot_mgr_unlock(rot_dev->mgr); @@ -2364,7 +2384,7 @@ static void sde_rotator_device_run(void *priv) /* disconnect request (will be freed by core layer) */ sde_rot_mgr_lock(rot_dev->mgr); - ctx->request->retireq = NULL; + ctx->request->retire_kw = NULL; ctx->request->retire_work = NULL; ctx->request = ERR_PTR(-EIO); sde_rot_mgr_unlock(rot_dev->mgr); @@ -2471,7 +2491,7 @@ static int sde_rotator_job_ready(void *priv) v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx), atomic_read(&ctx->command_pending)); atomic_inc(&ctx->command_pending); - queue_work(ctx->work_queue.rot_work_queue, &ctx->submit_work); + queue_kthread_work(&ctx->work_queue.rot_kw, &ctx->submit_work); } else if (!atomic_read(&ctx->request->pending_count)) { /* if pending request completed, forward to device run state */ SDEDEV_DBG(rot_dev->dev, diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h index c8dcdeee9ca0..8e4d86083508 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -21,6 +21,7 @@ #include <linux/iommu.h> #include <linux/dma-buf.h> #include <linux/msm-bus.h> +#include <linux/kthread.h> #include <media/v4l2-device.h> #include <media/v4l2-fh.h> #include <media/v4l2-ctrls.h> @@ -131,8 +132,8 @@ struct sde_rotator_ctx { struct sde_rotator_vbinfo *vbinfo_cap; struct sde_rotator_vbinfo *vbinfo_out; wait_queue_head_t wait_queue; - struct work_struct submit_work; - struct work_struct retire_work; + struct kthread_work submit_work; + struct kthread_work retire_work; struct sde_rot_queue work_queue; struct sde_rot_entry_container *request; struct sde_rot_file_private *private; diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c index 5b574ed9fabc..9e3d7d806a0c 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -145,7 +145,7 @@ static struct sde_mdp_hw_resource *sde_rotator_hw_alloc( struct sde_mdp_hw_resource *mdp_hw; struct sde_rot_data_type *mdata = sde_rot_get_mdata(); int pipe_ndx, offset = ctl_id; - int ret; + int ret = 0; mdp_hw = devm_kzalloc(&mgr->pdev->dev, sizeof(struct sde_mdp_hw_resource), GFP_KERNEL); diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c index 8157e8641e60..acb697946e18 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c @@ -1257,6 +1257,7 @@ err_put: data->srcp_dma_buf = NULL; imap_err: ion_free(rot->iclient, handle); + sde_smmu_ctrl(0); return rc; } @@ -1270,8 +1271,19 @@ static int sde_hw_rotator_swts_map(struct sde_hw_rotator *rot) { int rc = 0; struct sde_mdp_img_data *data = &rot->swts_buf; + struct sde_rot_data_type *mdata = sde_rot_get_mdata(); sde_smmu_ctrl(1); + if (mdata->wait_for_transition) { + rc = mdata->wait_for_transition(0, 0); + if (rc < 0) { + SDEROT_ERR("failed Secure wait for transition %d\n", + rc); + rc = -EPERM; + goto error; + } + } + rc = sde_smmu_map_dma_buf(data->srcp_dma_buf, data->srcp_table, SDE_IOMMU_DOMAIN_ROT_UNSECURE, &data->addr, &data->len, DMA_BIDIRECTIONAL); @@ -1291,7 +1303,7 @@ static int sde_hw_rotator_swts_map(struct sde_hw_rotator *rot) data->mapped = true; SDEROT_DBG("swts buffer mapped: %pad/%lx va:%p\n", &data->addr, - data->len, rot->swts_buffer); + data->len, rot->swts_buffer); sde_smmu_ctrl(0); return rc; @@ -1301,6 +1313,8 @@ kmap_err: err_unmap: dma_buf_unmap_attachment(data->srcp_attachment, data->srcp_table, DMA_FROM_DEVICE); +error: + sde_smmu_ctrl(0); return rc; } diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c index 7388dab92c34..037c6f3b12ab 100644 --- a/drivers/media/platform/msm/vidc/hfi_packetization.c +++ b/drivers/media/platform/msm/vidc/hfi_packetization.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1832,10 +1832,10 @@ int create_pkt_cmd_session_set_property( pkt->size += sizeof(u32) + sizeof(struct hfi_enable); break; } - case HAL_PARAM_VENC_H264_GENERATE_AUDNAL: + case HAL_PARAM_VENC_GENERATE_AUDNAL: { create_pkt_enable(pkt->rg_property_data, - HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL, + HFI_PROPERTY_PARAM_VENC_GENERATE_AUDNAL, ((struct hal_enable *)pdata)->enable); pkt->size += sizeof(u32) + sizeof(struct hfi_enable); break; diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c index c9dfb52861bc..1d30a869d754 100644 --- a/drivers/media/platform/msm/vidc/msm_smem.c +++ b/drivers/media/platform/msm/vidc/msm_smem.c @@ -490,11 +490,13 @@ bool msm_smem_compare_buffers(void *clt, int fd, void *priv) } static int ion_cache_operations(struct smem_client *client, - struct msm_smem *mem, enum smem_cache_ops cache_op) + struct msm_smem *mem, enum smem_cache_ops cache_op, + int size) { unsigned long ionflag = 0; int rc = 0; int msm_cache_ops = 0; + int op_size = 0; if (!mem || !client) { dprintk(VIDC_ERR, "Invalid params: %pK, %pK\n", mem, client); @@ -523,10 +525,15 @@ static int ion_cache_operations(struct smem_client *client, rc = -EINVAL; goto cache_op_failed; } + if (size <= 0) + op_size = mem->size; + else + op_size = mem->size < size ? mem->size : size; + rc = msm_ion_do_cache_offset_op(client->clnt, (struct ion_handle *)mem->smem_priv, 0, mem->offset, - (unsigned long)mem->size, msm_cache_ops); + (unsigned long)op_size, msm_cache_ops); if (rc) { dprintk(VIDC_ERR, "cache operation failed %d\n", rc); @@ -538,7 +545,7 @@ cache_op_failed: } int msm_smem_cache_operations(void *clt, struct msm_smem *mem, - enum smem_cache_ops cache_op) + enum smem_cache_ops cache_op, int size) { struct smem_client *client = clt; int rc = 0; @@ -549,7 +556,7 @@ int msm_smem_cache_operations(void *clt, struct msm_smem *mem, } switch (client->mem_type) { case SMEM_ION: - rc = ion_cache_operations(client, mem, cache_op); + rc = ion_cache_operations(client, mem, cache_op, size); if (rc) dprintk(VIDC_ERR, "Failed cache operations: %d\n", rc); diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c index a8dc1d010d62..de5a2dececdf 100644 --- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c @@ -230,6 +230,14 @@ static int msm_v4l2_queryctrl(struct file *file, void *fh, return msm_vidc_query_ctrl((void *)vidc_inst, ctrl); } +static int msm_v4l2_query_ext_ctrl(struct file *file, void *fh, + struct v4l2_query_ext_ctrl *ctrl) +{ + struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh); + + return msm_vidc_query_ext_ctrl((void *)vidc_inst, ctrl); +} + static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = { .vidioc_querycap = msm_v4l2_querycap, .vidioc_enum_fmt_vid_cap_mplane = msm_v4l2_enum_fmt, @@ -247,6 +255,7 @@ static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = { .vidioc_s_ctrl = msm_v4l2_s_ctrl, .vidioc_g_ctrl = msm_v4l2_g_ctrl, .vidioc_queryctrl = msm_v4l2_queryctrl, + .vidioc_query_ext_ctrl = msm_v4l2_query_ext_ctrl, .vidioc_s_ext_ctrls = msm_v4l2_s_ext_ctrl, .vidioc_subscribe_event = msm_v4l2_subscribe_event, .vidioc_unsubscribe_event = msm_v4l2_unsubscribe_event, @@ -321,6 +330,7 @@ static int msm_vidc_initialize_core(struct platform_device *pdev, init_completion(&core->completions[i]); } + msm_comm_sort_ctrl(); INIT_DELAYED_WORK(&core->fw_unload_work, msm_vidc_fw_unload_handler); return rc; } @@ -766,7 +776,6 @@ static int __init msm_vidc_init(void) if (rc) { dprintk(VIDC_ERR, "Failed to register platform driver\n"); - msm_vidc_debugfs_deinit_drv(); debugfs_remove_recursive(vidc_driver->debugfs_root); kfree(vidc_driver); vidc_driver = NULL; @@ -778,7 +787,6 @@ static int __init msm_vidc_init(void) static void __exit msm_vidc_exit(void) { platform_driver_unregister(&msm_vidc_driver); - msm_vidc_debugfs_deinit_drv(); debugfs_remove_recursive(vidc_driver->debugfs_root); mutex_destroy(&vidc_driver->lock); kfree(vidc_driver); diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index 953780e3c220..28a4006b480c 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.c @@ -11,6 +11,7 @@ * */ +#include <linux/sort.h> #include <linux/slab.h> #include <soc/qcom/scm.h> #include "msm_vidc_internal.h" @@ -18,6 +19,7 @@ #include "vidc_hfi_api.h" #include "msm_vidc_debug.h" #include "msm_vidc_dcvs.h" +#include "msm_vdec.h" #define MSM_VDEC_DVC_NAME "msm_vdec_8974" #define MIN_NUM_OUTPUT_BUFFERS 4 @@ -553,6 +555,7 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = { (1 << V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_TP10_UBWC) ), .qmenu = mpeg_vidc_video_dpb_color_format, + .flags = V4L2_CTRL_FLAG_MODIFY_LAYOUT, }, { .id = V4L2_CID_VIDC_QBUF_MODE, @@ -872,7 +875,7 @@ int msm_vdec_prepare_buf(struct msm_vidc_inst *inst, dprintk(VIDC_ERR, "Core %pK in bad state, ignoring prepare buf\n", inst->core); - goto exit; + return -EINVAL; } switch (b->type) { @@ -925,7 +928,7 @@ int msm_vdec_prepare_buf(struct msm_vidc_inst *inst, dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type); break; } -exit: + return rc; } @@ -1767,6 +1770,7 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count) struct msm_vidc_inst *inst; int rc = 0; struct hfi_device *hdev; + struct vb2_buffer *vb; struct vb2_buf_entry *temp, *next; if (!q || !q->drv_priv) { dprintk(VIDC_ERR, "Invalid input, q = %pK\n", q); @@ -1777,6 +1781,14 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count) dprintk(VIDC_ERR, "%s invalid parameters\n", __func__); return -EINVAL; } + + if (inst->state == MSM_VIDC_CORE_INVALID || + inst->core->state == VIDC_CORE_INVALID || + inst->core->state == VIDC_CORE_UNINIT) { + rc = -EINVAL; + goto stream_start_failed; + } + hdev = inst->core->device; dprintk(VIDC_DBG, "Streamon called on: %d capability for inst: %pK\n", q->type, inst); @@ -1791,8 +1803,7 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count) break; default: dprintk(VIDC_ERR, "Queue type is not supported: %d\n", q->type); - rc = -EINVAL; - goto stream_start_failed; + return -EINVAL; } if (rc) { dprintk(VIDC_ERR, @@ -1811,12 +1822,15 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count) stream_start_failed: if (rc) { + list_for_each_entry(vb, &q->queued_list, queued_entry) { + if (vb->type == q->type && + vb->state == VB2_BUF_STATE_ACTIVE) + vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED); + } mutex_lock(&inst->pendingq.lock); - list_for_each_entry_safe(temp, next, &inst->pendingq.list, - list) { + list_for_each_entry_safe(temp, next, + &inst->pendingq.list, list) { if (temp->vb->type == q->type) { - vb2_buffer_done(temp->vb, - VB2_BUF_STATE_QUEUED); list_del(&temp->list); kfree(temp); } @@ -2224,6 +2238,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) struct hal_enable_picture enable_picture; struct hal_enable hal_property; enum hal_property property_id = 0; + enum hal_video_codec codec; u32 property_val = 0; void *pdata = NULL; struct hfi_device *hdev; @@ -2278,12 +2293,23 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDC_VIDEO_PICTYPE_DEC_MODE: property_id = HAL_PARAM_VDEC_PICTURE_TYPE_DECODE; if (ctrl->val == - V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_ON) + V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_ON) { enable_picture.picture_type = HAL_PICTURE_I; - else - enable_picture.picture_type = HAL_PICTURE_I | - HAL_PICTURE_P | HAL_PICTURE_B | - HAL_PICTURE_IDR; + } else { + codec = get_hal_codec(inst->fmts[OUTPUT_PORT].fourcc); + if (codec == HAL_VIDEO_CODEC_H264) { + enable_picture.picture_type = HAL_PICTURE_I | + HAL_PICTURE_P | HAL_PICTURE_B | + HAL_PICTURE_IDR; + } else if (codec == HAL_VIDEO_CODEC_HEVC) { + enable_picture.picture_type = HAL_PICTURE_I | + HAL_PICTURE_P | HAL_PICTURE_B | + HAL_PICTURE_IDR | HAL_PICTURE_CRA; + } else { + enable_picture.picture_type = HAL_PICTURE_I | + HAL_PICTURE_P | HAL_PICTURE_B; + } + } pdata = &enable_picture; break; case V4L2_CID_MPEG_VIDC_VIDEO_KEEP_ASPECT_RATIO: @@ -2791,3 +2817,21 @@ int msm_vdec_ctrl_init(struct msm_vidc_inst *inst) return msm_comm_ctrl_init(inst, msm_vdec_ctrls, ARRAY_SIZE(msm_vdec_ctrls), &msm_vdec_ctrl_ops); } + +void msm_vdec_g_ctrl(struct msm_vidc_ctrl **ctrls, int *num_ctrls) +{ + *ctrls = msm_vdec_ctrls; + *num_ctrls = NUM_CTRLS; +} + +static int msm_vdec_ctrl_cmp(const void *st1, const void *st2) +{ + return (int32_t)((struct msm_vidc_ctrl *)st1)->id - + (int32_t)((struct msm_vidc_ctrl *)st2)->id; +} + +void msm_vdec_ctrl_sort(void) +{ + sort(msm_vdec_ctrls, NUM_CTRLS, sizeof(struct msm_vidc_ctrl), + msm_vdec_ctrl_cmp, NULL); +} diff --git a/drivers/media/platform/msm/vidc/msm_vdec.h b/drivers/media/platform/msm/vidc/msm_vdec.h index 47426c143c08..227cc99242d8 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.h +++ b/drivers/media/platform/msm/vidc/msm_vdec.h @@ -18,12 +18,13 @@ int msm_vdec_inst_init(struct msm_vidc_inst *inst); int msm_vdec_ctrl_init(struct msm_vidc_inst *inst); -int msm_vdec_querycap(void *instance, struct v4l2_capability *cap); -int msm_vdec_enum_fmt(void *instance, struct v4l2_fmtdesc *f); -int msm_vdec_s_fmt(void *instance, struct v4l2_format *f); -int msm_vdec_g_fmt(void *instance, struct v4l2_format *f); -int msm_vdec_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a); -int msm_vdec_reqbufs(void *instance, struct v4l2_requestbuffers *b); +int msm_vdec_querycap(struct msm_vidc_inst *inst, struct v4l2_capability *cap); +int msm_vdec_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f); +int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f); +int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f); +int msm_vdec_s_ext_ctrl(struct msm_vidc_inst *inst, + struct v4l2_ext_controls *a); +int msm_vdec_reqbufs(struct msm_vidc_inst *inst, struct v4l2_requestbuffers *b); int msm_vdec_prepare_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b); int msm_vdec_release_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b); int msm_vdec_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b); @@ -32,6 +33,8 @@ int msm_vdec_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i); int msm_vdec_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i); int msm_vdec_cmd(struct msm_vidc_inst *inst, struct v4l2_decoder_cmd *dec); int msm_vdec_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a); -struct vb2_ops *msm_vdec_get_vb2q_ops(void); +const struct vb2_ops *msm_vdec_get_vb2q_ops(void); +void msm_vdec_g_ctrl(struct msm_vidc_ctrl **ctrls, int *num_ctrls); +void msm_vdec_ctrl_sort(void); #endif diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c index 30726354164b..c5816511e056 100644 --- a/drivers/media/platform/msm/vidc/msm_venc.c +++ b/drivers/media/platform/msm/vidc/msm_venc.c @@ -54,12 +54,12 @@ * 3x3 transformation matrix coefficients in s4.9 fixed point format */ static u32 vpe_csc_601_to_709_matrix_coeff[HAL_MAX_MATRIX_COEFFS] = { - 470, 8170, 8148, 0, 490, 50, 0, 34, 483 + 440, 8140, 8098, 0, 460, 52, 0, 34, 463 }; /* offset coefficients in s9 fixed point format */ static u32 vpe_csc_601_to_709_bias_coeff[HAL_MAX_BIAS_COEFFS] = { - 34, 0, 4 + 53, 0, 4 }; /* clamping value for Y/U/V([min,max] for Y/U/V) */ @@ -861,14 +861,14 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = { .step = 1, }, { - .id = V4L2_CID_MPEG_VIDC_VIDEO_H264_AU_DELIMITER, + .id = V4L2_CID_MPEG_VIDC_VIDEO_AU_DELIMITER, .name = "H264 AU Delimiter", .type = V4L2_CTRL_TYPE_BOOLEAN, - .minimum = V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_DISABLED, - .maximum = V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_ENABLED, + .minimum = V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_DISABLED, + .maximum = V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_ENABLED, .step = 1, .default_value = - V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_DISABLED, + V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_DISABLED, }, { .id = V4L2_CID_MPEG_VIDC_SET_PERF_LEVEL, @@ -1897,12 +1897,22 @@ static int msm_venc_start_streaming(struct vb2_queue *q, unsigned int count) { struct msm_vidc_inst *inst; int rc = 0; + struct vb2_buffer *vb; struct vb2_buf_entry *temp, *next; + if (!q || !q->drv_priv) { dprintk(VIDC_ERR, "Invalid input, q = %pK\n", q); return -EINVAL; } inst = q->drv_priv; + + if (inst->state == MSM_VIDC_CORE_INVALID || + inst->core->state == VIDC_CORE_INVALID || + inst->core->state == VIDC_CORE_UNINIT) { + rc = -EINVAL; + goto stream_start_failed; + } + dprintk(VIDC_DBG, "Streamon called on: %d capability for inst: %pK\n", q->type, inst); switch (q->type) { @@ -1916,8 +1926,7 @@ static int msm_venc_start_streaming(struct vb2_queue *q, unsigned int count) break; default: dprintk(VIDC_ERR, "Queue type is not supported: %d\n", q->type); - rc = -EINVAL; - goto stream_start_failed; + return -EINVAL; } if (rc) { dprintk(VIDC_ERR, @@ -1936,12 +1945,15 @@ static int msm_venc_start_streaming(struct vb2_queue *q, unsigned int count) stream_start_failed: if (rc) { + list_for_each_entry(vb, &q->queued_list, queued_entry) { + if (vb->type == q->type && + vb->state == VB2_BUF_STATE_ACTIVE) + vb2_buffer_done(vb, VB2_BUF_STATE_QUEUED); + } mutex_lock(&inst->pendingq.lock); - list_for_each_entry_safe(temp, next, &inst->pendingq.list, - list) { + list_for_each_entry_safe(temp, next, + &inst->pendingq.list, list) { if (temp->vb->type == q->type) { - vb2_buffer_done(temp->vb, - VB2_BUF_STATE_QUEUED); list_del(&temp->list); kfree(temp); } @@ -3305,14 +3317,14 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl) pdata = &vui_timing_info; break; } - case V4L2_CID_MPEG_VIDC_VIDEO_H264_AU_DELIMITER: - property_id = HAL_PARAM_VENC_H264_GENERATE_AUDNAL; + case V4L2_CID_MPEG_VIDC_VIDEO_AU_DELIMITER: + property_id = HAL_PARAM_VENC_GENERATE_AUDNAL; switch (ctrl->val) { - case V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_DISABLED: + case V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_DISABLED: enable.enable = 0; break; - case V4L2_MPEG_VIDC_VIDEO_H264_AU_DELIMITER_ENABLED: + case V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_ENABLED: enable.enable = 1; break; default: @@ -4425,7 +4437,7 @@ int msm_venc_prepare_buf(struct msm_vidc_inst *inst, dprintk(VIDC_ERR, "Core %pK in bad state, ignoring prepare buf\n", inst->core); - goto exit; + return -EINVAL; } switch (b->type) { @@ -4473,7 +4485,7 @@ int msm_venc_prepare_buf(struct msm_vidc_inst *inst, "Buffer type not recognized: %d\n", b->type); break; } -exit: + return rc; } diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c index 1ff2ca4cb91f..7caf61cb6799 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc.c +++ b/drivers/media/platform/msm/vidc/msm_vidc.c @@ -14,13 +14,14 @@ #include <linux/dma-direction.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/bsearch.h> +#include <linux/delay.h> #include <media/msm_vidc.h> #include "msm_vidc_internal.h" #include "msm_vidc_debug.h" #include "msm_vdec.h" #include "msm_venc.h" #include "msm_vidc_common.h" -#include <linux/delay.h> #include "vidc_hfi_api.h" #include "msm_vidc_dcvs.h" @@ -88,7 +89,7 @@ int msm_vidc_querycap(void *instance, struct v4l2_capability *cap) return -EINVAL; if (inst->session_type == MSM_VIDC_DECODER) - return msm_vdec_querycap(instance, cap); + return msm_vdec_querycap(inst, cap); else if (inst->session_type == MSM_VIDC_ENCODER) return msm_venc_querycap(instance, cap); return -EINVAL; @@ -103,7 +104,7 @@ int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f) return -EINVAL; if (inst->session_type == MSM_VIDC_DECODER) - return msm_vdec_enum_fmt(instance, f); + return msm_vdec_enum_fmt(inst, f); else if (inst->session_type == MSM_VIDC_ENCODER) return msm_venc_enum_fmt(instance, f); return -EINVAL; @@ -138,6 +139,101 @@ int msm_vidc_query_ctrl(void *instance, struct v4l2_queryctrl *ctrl) } EXPORT_SYMBOL(msm_vidc_query_ctrl); +static int msm_vidc_queryctrl_bsearch_cmp1(const void *key, const void *elt) +{ + return *(int32_t *)key - (int32_t)((struct msm_vidc_ctrl *)elt)->id; +} + +static int msm_vidc_queryctrl_bsearch_cmp2(const void *key, const void *elt) +{ + uint32_t id = *(uint32_t *)key; + struct msm_vidc_ctrl *ctrl = (struct msm_vidc_ctrl *)elt; + + if (id >= ctrl[0].id && id < ctrl[1].id) + return 0; + else if (id < ctrl[0].id) + return -1; + else + return 1; +} + +int msm_vidc_query_ext_ctrl(void *instance, struct v4l2_query_ext_ctrl *ctrl) +{ + struct msm_vidc_inst *inst = instance; + bool get_next_ctrl = 0; + int i, num_ctrls, rc = 0; + struct msm_vidc_ctrl *key = NULL; + struct msm_vidc_ctrl *msm_vdec_ctrls; + + if (!inst || !ctrl) + return -EINVAL; + + i = ctrl->id; + memset(ctrl, 0, sizeof(struct v4l2_query_ext_ctrl)); + ctrl->id = i; + + if (ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL) + get_next_ctrl = 1; + else if (ctrl->id & V4L2_CTRL_FLAG_NEXT_COMPOUND) + goto query_ext_ctrl_err; + + ctrl->id &= ~V4L2_CTRL_FLAG_NEXT_CTRL; + ctrl->id &= ~V4L2_CTRL_FLAG_NEXT_COMPOUND; + + if (ctrl->id > V4L2_CID_PRIVATE_BASE || + (ctrl->id >= V4L2_CID_BASE && ctrl->id <= V4L2_CID_LASTP1)) + goto query_ext_ctrl_err; + else if (ctrl->id == V4L2_CID_PRIVATE_BASE && get_next_ctrl) + ctrl->id = V4L2_CID_MPEG_MSM_VIDC_BASE; + + if (inst->session_type == MSM_VIDC_DECODER) + msm_vdec_g_ctrl(&msm_vdec_ctrls, &num_ctrls); + else + return -EINVAL; + + if (!get_next_ctrl) + key = bsearch(&ctrl->id, msm_vdec_ctrls, num_ctrls, + sizeof(struct msm_vidc_ctrl), + msm_vidc_queryctrl_bsearch_cmp1); + else { + key = bsearch(&ctrl->id, msm_vdec_ctrls, num_ctrls-1, + sizeof(struct msm_vidc_ctrl), + msm_vidc_queryctrl_bsearch_cmp2); + + if (key && ctrl->id > key->id) + key++; + if (key) { + for (i = key-msm_vdec_ctrls, key = NULL; + i < num_ctrls; i++) + if (!(msm_vdec_ctrls[i].flags & + V4L2_CTRL_FLAG_DISABLED)) { + key = &msm_vdec_ctrls[i]; + break; + } + } + } + + if (key) { + ctrl->id = key->id; + ctrl->type = key->type; + strlcpy(ctrl->name, key->name, MAX_NAME_LENGTH); + ctrl->minimum = key->minimum; + ctrl->maximum = key->maximum; + ctrl->step = key->step; + ctrl->default_value = key->default_value; + ctrl->flags = key->flags; + ctrl->elems = 1; + ctrl->nr_of_dims = 0; + return rc; + } + +query_ext_ctrl_err: + ctrl->name[0] = '\0'; + ctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return -EINVAL; +} +EXPORT_SYMBOL(msm_vidc_query_ext_ctrl); + int msm_vidc_s_fmt(void *instance, struct v4l2_format *f) { struct msm_vidc_inst *inst = instance; @@ -146,7 +242,7 @@ int msm_vidc_s_fmt(void *instance, struct v4l2_format *f) return -EINVAL; if (inst->session_type == MSM_VIDC_DECODER) - return msm_vdec_s_fmt(instance, f); + return msm_vdec_s_fmt(inst, f); if (inst->session_type == MSM_VIDC_ENCODER) return msm_venc_s_fmt(instance, f); return -EINVAL; @@ -161,7 +257,7 @@ int msm_vidc_g_fmt(void *instance, struct v4l2_format *f) return -EINVAL; if (inst->session_type == MSM_VIDC_DECODER) - return msm_vdec_g_fmt(instance, f); + return msm_vdec_g_fmt(inst, f); else if (inst->session_type == MSM_VIDC_ENCODER) return msm_venc_g_fmt(instance, f); return -EINVAL; @@ -197,7 +293,7 @@ int msm_vidc_s_ext_ctrl(void *instance, struct v4l2_ext_controls *control) return -EINVAL; if (inst->session_type == MSM_VIDC_DECODER) - return msm_vdec_s_ext_ctrl(instance, control); + return msm_vdec_s_ext_ctrl(inst, control); if (inst->session_type == MSM_VIDC_ENCODER) return msm_venc_s_ext_ctrl(instance, control); return -EINVAL; @@ -212,7 +308,7 @@ int msm_vidc_reqbufs(void *instance, struct v4l2_requestbuffers *b) return -EINVAL; if (inst->session_type == MSM_VIDC_DECODER) - return msm_vdec_reqbufs(instance, b); + return msm_vdec_reqbufs(inst, b); if (inst->session_type == MSM_VIDC_ENCODER) return msm_venc_reqbufs(instance, b); return -EINVAL; @@ -435,13 +531,60 @@ static inline void save_v4l2_buffer(struct v4l2_buffer *b, } } +static int __map_and_update_binfo(struct msm_vidc_inst *inst, + struct buffer_info *binfo, + struct v4l2_buffer *b, int i) +{ + int rc = 0; + struct msm_smem *same_fd_handle = NULL; + + same_fd_handle = get_same_fd_buffer( + inst, b->m.planes[i].reserved[0]); + + if (same_fd_handle) { + binfo->device_addr[i] = + same_fd_handle->device_addr + binfo->buff_off[i]; + b->m.planes[i].m.userptr = binfo->device_addr[i]; + binfo->handle[i] = same_fd_handle; + } else { + binfo->handle[i] = map_buffer(inst, &b->m.planes[i], + get_hal_buffer_type(inst, b)); + if (!binfo->handle[i]) + rc = -EINVAL; + + binfo->mapped[i] = true; + binfo->device_addr[i] = binfo->handle[i]->device_addr + + binfo->buff_off[i]; + b->m.planes[i].m.userptr = binfo->device_addr[i]; + } + + return rc; +} + +static int __handle_fw_referenced_buffers(struct msm_vidc_inst *inst, + struct buffer_info *binfo, + struct v4l2_buffer *b) +{ + int i = 0, rc = 0; + + if (EXTRADATA_IDX(b->length)) { + i = EXTRADATA_IDX(b->length); + if (b->m.planes[i].length) + rc = __map_and_update_binfo(inst, binfo, b, i); + } + + if (rc) + dprintk(VIDC_ERR, "%s: Failed to map extradata\n", __func__); + + return rc; +} + int map_and_register_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b) { struct buffer_info *binfo = NULL; struct buffer_info *temp = NULL, *iterator = NULL; int plane = 0; int i = 0, rc = 0; - struct msm_smem *same_fd_handle = NULL; if (!b || !inst) { dprintk(VIDC_ERR, "%s: invalid input\n", __func__); @@ -517,33 +660,17 @@ int map_and_register_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b) rc = 0; goto exit; } else if (rc == 2) { - rc = -EEXIST; + rc = __handle_fw_referenced_buffers(inst, temp, b); + if (!rc) + rc = -EEXIST; goto exit; } - same_fd_handle = get_same_fd_buffer( - inst, b->m.planes[i].reserved[0]); - populate_buf_info(binfo, b, i); - if (same_fd_handle) { - binfo->device_addr[i] = - same_fd_handle->device_addr + binfo->buff_off[i]; - b->m.planes[i].m.userptr = binfo->device_addr[i]; - binfo->mapped[i] = false; - binfo->handle[i] = same_fd_handle; - } else { - binfo->handle[i] = map_buffer(inst, &b->m.planes[i], - get_hal_buffer_type(inst, b)); - if (!binfo->handle[i]) { - rc = -EINVAL; - goto exit; - } - binfo->mapped[i] = true; - binfo->device_addr[i] = binfo->handle[i]->device_addr + - binfo->buff_off[i]; - b->m.planes[i].m.userptr = binfo->device_addr[i]; - } + rc = __map_and_update_binfo(inst, binfo, b, i); + if (rc) + goto exit; /* We maintain one ref count for all planes*/ if (!i && is_dynamic_output_buffer_mode(b, inst)) { @@ -631,6 +758,7 @@ int unmap_and_deregister_buf(struct msm_vidc_inst *inst, temp->handle[i] = 0; temp->device_addr[i] = 0; temp->uvaddr[i] = 0; + temp->mapped[i] = false; } } if (!keep_node) { @@ -670,10 +798,11 @@ int qbuf_dynamic_buf(struct msm_vidc_inst *inst, } int output_buffer_cache_invalidate(struct msm_vidc_inst *inst, - struct buffer_info *binfo) + struct buffer_info *binfo, struct v4l2_buffer *b) { int i = 0; int rc = 0; + int size = -1; if (!inst) { dprintk(VIDC_ERR, "%s: invalid inst: %pK\n", __func__, inst); @@ -686,23 +815,34 @@ int output_buffer_cache_invalidate(struct msm_vidc_inst *inst, return -EINVAL; } - for (i = 0; i < binfo->num_planes; i++) { - if (binfo->handle[i]) { - struct msm_smem smem = *binfo->handle[i]; - - smem.offset = (unsigned int)(binfo->buff_off[i]); - smem.size = binfo->size[i]; - rc = msm_comm_smem_cache_operations(inst, - &smem, SMEM_CACHE_INVALIDATE); - if (rc) { - dprintk(VIDC_ERR, - "%s: Failed to clean caches: %d\n", - __func__, rc); - return -EINVAL; - } - } else - dprintk(VIDC_DBG, "%s: NULL handle for plane %d\n", + if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + for (i = 0; i < binfo->num_planes; i++) { + if (binfo->handle[i]) { + struct msm_smem smem = *binfo->handle[i]; + + if (inst->session_type == MSM_VIDC_ENCODER && + !i) + size = b->m.planes[i].bytesused; + else + size = -1; + + smem.offset = + (unsigned int)(binfo->buff_off[i]); + smem.size = binfo->size[i]; + rc = msm_comm_smem_cache_operations(inst, + &smem, SMEM_CACHE_INVALIDATE, + size); + if (rc) { + dprintk(VIDC_ERR, + "%s: Failed to clean caches: %d\n", + __func__, rc); + return -EINVAL; + } + } else + dprintk(VIDC_DBG, + "%s: NULL handle for plane %d\n", __func__, i); + } } return 0; } @@ -737,7 +877,7 @@ int msm_vidc_prepare_buf(void *instance, struct v4l2_buffer *b) return -EINVAL; if (inst->session_type == MSM_VIDC_DECODER) - return msm_vdec_prepare_buf(instance, b); + return msm_vdec_prepare_buf(inst, b); if (inst->session_type == MSM_VIDC_ENCODER) return msm_venc_prepare_buf(instance, b); return -EINVAL; @@ -804,8 +944,7 @@ int msm_vidc_release_buffers(void *instance, int buffer_type) if (!release_buf) continue; if (inst->session_type == MSM_VIDC_DECODER) - rc = msm_vdec_release_buf(instance, - &buffer_info); + rc = msm_vdec_release_buf(inst, &buffer_info); if (inst->session_type == MSM_VIDC_ENCODER) rc = msm_venc_release_buf(instance, &buffer_info); @@ -858,6 +997,7 @@ int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b) int plane = 0; int rc = 0; int i; + int size = -1; if (!inst || !inst->core || !b || !valid_v4l2_buffer(b, inst)) return -EINVAL; @@ -905,7 +1045,7 @@ int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b) V4L2_PIX_FMT_HEVC_HYBRID && binfo->handle[i] && b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { rc = msm_comm_smem_cache_operations(inst, - binfo->handle[i], SMEM_CACHE_INVALIDATE); + binfo->handle[i], SMEM_CACHE_INVALIDATE, -1); if (rc) { dprintk(VIDC_ERR, "Failed to inv caches: %d\n", rc); @@ -915,8 +1055,13 @@ int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b) if (binfo->handle[i] && (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) { + if (inst->session_type == MSM_VIDC_DECODER && !i) + size = b->m.planes[i].bytesused; + else + size = -1; rc = msm_comm_smem_cache_operations(inst, - binfo->handle[i], SMEM_CACHE_CLEAN); + binfo->handle[i], SMEM_CACHE_CLEAN, + size); if (rc) { dprintk(VIDC_ERR, "Failed to clean caches: %d\n", rc); @@ -926,7 +1071,7 @@ int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b) } if (inst->session_type == MSM_VIDC_DECODER) - return msm_vdec_qbuf(instance, b); + return msm_vdec_qbuf(inst, b); if (inst->session_type == MSM_VIDC_ENCODER) return msm_venc_qbuf(instance, b); @@ -985,7 +1130,7 @@ int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b) return -EINVAL; } - rc = output_buffer_cache_invalidate(inst, buffer_info); + rc = output_buffer_cache_invalidate(inst, buffer_info, b); if (rc) return rc; @@ -1012,7 +1157,7 @@ int msm_vidc_streamon(void *instance, enum v4l2_buf_type i) return -EINVAL; if (inst->session_type == MSM_VIDC_DECODER) - return msm_vdec_streamon(instance, i); + return msm_vdec_streamon(inst, i); if (inst->session_type == MSM_VIDC_ENCODER) return msm_venc_streamon(instance, i); return -EINVAL; @@ -1027,7 +1172,7 @@ int msm_vidc_streamoff(void *instance, enum v4l2_buf_type i) return -EINVAL; if (inst->session_type == MSM_VIDC_DECODER) - return msm_vdec_streamoff(instance, i); + return msm_vdec_streamoff(inst, i); if (inst->session_type == MSM_VIDC_ENCODER) return msm_venc_streamoff(instance, i); return -EINVAL; @@ -1347,8 +1492,6 @@ static void cleanup_instance(struct msm_vidc_inst *inst) "Failed to release output buffers\n"); } - debugfs_remove_recursive(inst->debugfs_root); - mutex_lock(&inst->pending_getpropq.lock); if (!list_empty(&inst->pending_getpropq.list)) { dprintk(VIDC_ERR, @@ -1362,7 +1505,6 @@ static void cleanup_instance(struct msm_vidc_inst *inst) int msm_vidc_destroy(struct msm_vidc_inst *inst) { struct msm_vidc_core *core; - int i = 0; if (!inst || !inst->core) return -EINVAL; @@ -1386,14 +1528,13 @@ int msm_vidc_destroy(struct msm_vidc_inst *inst) v4l2_fh_del(&inst->event_handler); v4l2_fh_exit(&inst->event_handler); - for (i = 0; i < MAX_PORT_NUM; i++) - vb2_queue_release(&inst->bufq[i].vb2_bufq); - mutex_destroy(&inst->sync_lock); mutex_destroy(&inst->bufq[CAPTURE_PORT].lock); mutex_destroy(&inst->bufq[OUTPUT_PORT].lock); mutex_destroy(&inst->lock); + msm_vidc_debugfs_deinit_inst(inst); + pr_info(VIDC_DBG_TAG "Closed video instance: %pK\n", VIDC_MSG_PRIO2STRING(VIDC_INFO), inst); kfree(inst); @@ -1412,7 +1553,7 @@ int msm_vidc_close(void *instance) struct msm_vidc_inst *inst = instance; struct buffer_info *bi, *dummy; - int rc = 0; + int rc = 0, i = 0; if (!inst || !inst->core) return -EINVAL; @@ -1449,6 +1590,12 @@ int msm_vidc_close(void *instance) msm_comm_session_clean(inst); msm_smem_delete_client(inst->mem_client); + for (i = 0; i < MAX_PORT_NUM; i++) { + mutex_lock(&inst->bufq[i].lock); + vb2_queue_release(&inst->bufq[i].vb2_bufq); + mutex_unlock(&inst->bufq[i].lock); + } + kref_put(&inst->kref, close_helper); return 0; } diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 7b28e80979f2..f85fe6fd3867 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c @@ -22,6 +22,7 @@ #include "vidc_hfi_api.h" #include "msm_vidc_debug.h" #include "msm_vidc_dcvs.h" +#include "msm_vdec.h" #define IS_ALREADY_IN_STATE(__p, __d) ({\ int __rc = (__p >= __d);\ @@ -3174,7 +3175,7 @@ static int set_output_buffers(struct msm_vidc_inst *inst, goto err_no_mem; } rc = msm_comm_smem_cache_operations(inst, - handle, SMEM_CACHE_CLEAN); + handle, SMEM_CACHE_CLEAN, -1); if (rc) { dprintk(VIDC_WARN, "Failed to clean cache may cause undefined behavior\n"); @@ -3265,7 +3266,7 @@ static int set_internal_buf_on_fw(struct msm_vidc_inst *inst, hdev = inst->core->device; rc = msm_comm_smem_cache_operations(inst, - handle, SMEM_CACHE_CLEAN); + handle, SMEM_CACHE_CLEAN, -1); if (rc) { dprintk(VIDC_WARN, "Failed to clean cache. Undefined behavior\n"); @@ -4524,10 +4525,15 @@ static void msm_comm_flush_in_invalid_state(struct msm_vidc_inst *inst) struct vb2_buffer *vb = container_of(ptr, struct vb2_buffer, queued_entry); - vb->planes[0].bytesused = 0; - vb->planes[0].data_offset = 0; - - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + if (vb->state == VB2_BUF_STATE_ACTIVE) { + vb->planes[0].bytesused = 0; + vb->planes[0].data_offset = 0; + vb2_buffer_done(vb, VB2_BUF_STATE_DONE); + } else { + dprintk(VIDC_WARN, + "%s VB is in state %d not in ACTIVE state\n" + , __func__, vb->state); + } } mutex_unlock(&inst->bufq[port].lock); } @@ -5154,14 +5160,16 @@ void msm_comm_smem_free(struct msm_vidc_inst *inst, struct msm_smem *mem) } int msm_comm_smem_cache_operations(struct msm_vidc_inst *inst, - struct msm_smem *mem, enum smem_cache_ops cache_ops) + struct msm_smem *mem, enum smem_cache_ops cache_ops, + int size) { if (!inst || !mem) { dprintk(VIDC_ERR, "%s: invalid params: %pK %pK\n", __func__, inst, mem); return -EINVAL; } - return msm_smem_cache_operations(inst->mem_client, mem, cache_ops); + return msm_smem_cache_operations(inst->mem_client, mem, + cache_ops, size); } struct msm_smem *msm_comm_smem_user_to_kernel(struct msm_vidc_inst *inst, @@ -5426,3 +5434,7 @@ static void msm_comm_print_debug_info(struct msm_vidc_inst *inst) } mutex_unlock(&core->lock); } +void msm_comm_sort_ctrl(void) +{ + msm_vdec_ctrl_sort(); +} diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h index eac7f658eb31..7d28859c040c 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -76,7 +76,7 @@ struct msm_smem *msm_comm_smem_alloc(struct msm_vidc_inst *inst, enum hal_buffer buffer_type, int map_kernel); void msm_comm_smem_free(struct msm_vidc_inst *inst, struct msm_smem *mem); int msm_comm_smem_cache_operations(struct msm_vidc_inst *inst, - struct msm_smem *mem, enum smem_cache_ops cache_ops); + struct msm_smem *mem, enum smem_cache_ops cache_ops, int size); struct msm_smem *msm_comm_smem_user_to_kernel(struct msm_vidc_inst *inst, int fd, u32 offset, enum hal_buffer buffer_type); enum hal_video_codec get_hal_codec(int fourcc); @@ -99,4 +99,5 @@ void msm_comm_cleanup_internal_buffers(struct msm_vidc_inst *inst); int msm_vidc_comm_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a); bool msm_comm_turbo_session(struct msm_vidc_inst *inst); void msm_comm_print_inst_info(struct msm_vidc_inst *inst); +void msm_comm_sort_ctrl(void); #endif diff --git a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c index 3e269576c126..9bc313adb10a 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_dcvs.c @@ -407,8 +407,10 @@ static int msm_dcvs_enc_scale_clocks(struct msm_vidc_inst *inst) if (dcvs->etb_counter < total_input_buf) { dcvs->etb_counter++; - if (dcvs->etb_counter != total_input_buf) - return rc; + if (dcvs->etb_counter != total_input_buf) { + return msm_comm_scale_clocks_load(core, dcvs->load, + LOAD_CALC_NO_QUIRKS); + } } dprintk(VIDC_PROF, @@ -425,7 +427,7 @@ static int msm_dcvs_enc_scale_clocks(struct msm_vidc_inst *inst) } if (fw_pending_bufs >= DCVS_ENC_HIGH_THR && - dcvs->load <= dcvs->load_low) { + dcvs->load < dcvs->load_high) { dcvs->load = dcvs->load_high; dcvs->prev_freq_increased = true; } else { diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c index a9b367d6fe93..5c13b6fef3ec 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c @@ -12,6 +12,7 @@ */ #define CREATE_TRACE_POINTS +#define MAX_SSR_STRING_LEN 10 #include "msm_vidc_debug.h" #include "vidc_hfi_api.h" @@ -37,44 +38,31 @@ bool msm_vidc_debug_timeout = false; #define MAX_DBG_BUF_SIZE 4096 -struct debug_buffer { - struct mutex lock; - char ptr[MAX_DBG_BUF_SIZE]; - char *curr; - u32 filled_size; -}; - -static struct debug_buffer dbg_buf; - -#define INIT_DBG_BUF(__buf) ({ \ - __buf.curr = __buf.ptr;\ - __buf.filled_size = 0; \ -}) - #define DYNAMIC_BUF_OWNER(__binfo) ({ \ atomic_read(&__binfo->ref_count) == 2 ? "video driver" : "firmware";\ }) +struct core_inst_pair { + struct msm_vidc_core *core; + struct msm_vidc_inst *inst; +}; + static int core_info_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; return 0; } -static u32 write_str(struct debug_buffer *buffer, const char *fmt, ...) +static u32 write_str(char *buffer, + size_t size, const char *fmt, ...) { va_list args; - u32 size; - - char *curr = buffer->curr; - char *end = buffer->ptr + MAX_DBG_BUF_SIZE; + u32 len; va_start(args, fmt); - size = vscnprintf(curr, end - curr, fmt, args); + len = vscnprintf(buffer, size, fmt, args); va_end(args); - buffer->curr += size; - buffer->filled_size += size; - return size; + return len; } static ssize_t core_info_read(struct file *file, char __user *buf, @@ -83,6 +71,7 @@ static ssize_t core_info_read(struct file *file, char __user *buf, struct msm_vidc_core *core = file->private_data; struct hfi_device *hdev; struct hal_fw_info fw_info = { {0} }; + char *dbuf, *cur, *end; int i = 0, rc = 0; ssize_t len = 0; @@ -90,36 +79,46 @@ static ssize_t core_info_read(struct file *file, char __user *buf, dprintk(VIDC_ERR, "Invalid params, core: %pK\n", core); return 0; } + + dbuf = kzalloc(MAX_DBG_BUF_SIZE, GFP_KERNEL); + if (!dbuf) { + dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__); + return -ENOMEM; + } + cur = dbuf; + end = cur + MAX_DBG_BUF_SIZE; hdev = core->device; - mutex_lock(&dbg_buf.lock); - INIT_DBG_BUF(dbg_buf); - write_str(&dbg_buf, "===============================\n"); - write_str(&dbg_buf, "CORE %d: %pK\n", core->id, core); - write_str(&dbg_buf, "===============================\n"); - write_str(&dbg_buf, "Core state: %d\n", core->state); + cur += write_str(cur, end - cur, "===============================\n"); + cur += write_str(cur, end - cur, "CORE %d: %pK\n", core->id, core); + cur += write_str(cur, end - cur, "===============================\n"); + cur += write_str(cur, end - cur, "Core state: %d\n", core->state); rc = call_hfi_op(hdev, get_fw_info, hdev->hfi_device_data, &fw_info); if (rc) { dprintk(VIDC_WARN, "Failed to read FW info\n"); goto err_fw_info; } - write_str(&dbg_buf, "FW version : %s\n", &fw_info.version); - write_str(&dbg_buf, "base addr: 0x%x\n", fw_info.base_addr); - write_str(&dbg_buf, "register_base: 0x%x\n", fw_info.register_base); - write_str(&dbg_buf, "register_size: %u\n", fw_info.register_size); - write_str(&dbg_buf, "irq: %u\n", fw_info.irq); + cur += write_str(cur, end - cur, + "FW version : %s\n", &fw_info.version); + cur += write_str(cur, end - cur, + "base addr: 0x%x\n", fw_info.base_addr); + cur += write_str(cur, end - cur, + "register_base: 0x%x\n", fw_info.register_base); + cur += write_str(cur, end - cur, + "register_size: %u\n", fw_info.register_size); + cur += write_str(cur, end - cur, "irq: %u\n", fw_info.irq); err_fw_info: for (i = SYS_MSG_START; i < SYS_MSG_END; i++) { - write_str(&dbg_buf, "completions[%d]: %s\n", i, + cur += write_str(cur, end - cur, "completions[%d]: %s\n", i, completion_done(&core->completions[SYS_MSG_INDEX(i)]) ? "pending" : "done"); } len = simple_read_from_buffer(buf, count, ppos, - dbg_buf.ptr, dbg_buf.filled_size); + dbuf, cur - dbuf); - mutex_unlock(&dbg_buf.lock); + kfree(dbuf); return len; } @@ -136,17 +135,33 @@ static int trigger_ssr_open(struct inode *inode, struct file *file) static ssize_t trigger_ssr_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { - u32 ssr_trigger_val; - int rc; + unsigned long ssr_trigger_val = 0; + int rc = 0; struct msm_vidc_core *core = filp->private_data; - rc = sscanf(buf, "%d", &ssr_trigger_val); - if (rc < 0) { + size_t size = MAX_SSR_STRING_LEN; + char kbuf[MAX_SSR_STRING_LEN + 1] = {0}; + + if (!count) + goto exit; + + if (count < size) + size = count; + + if (copy_from_user(kbuf, buf, size)) { + dprintk(VIDC_WARN, "%s User memory fault\n", __func__); + rc = -EFAULT; + goto exit; + } + + rc = kstrtoul(kbuf, 0, &ssr_trigger_val); + if (rc) { dprintk(VIDC_WARN, "returning error err %d\n", rc); rc = -EINVAL; } else { msm_vidc_trigger_ssr(core, ssr_trigger_val); rc = count; } +exit: return rc; } @@ -160,7 +175,6 @@ struct dentry *msm_vidc_debugfs_init_drv(void) bool ok = false; struct dentry *dir = NULL; - mutex_init(&dbg_buf.lock); dir = debugfs_create_dir("msm_vidc", NULL); if (IS_ERR_OR_NULL(dir)) { dir = NULL; @@ -246,12 +260,15 @@ failed_create_dir: static int inst_info_open(struct inode *inode, struct file *file) { + dprintk(VIDC_INFO, "Open inode ptr: %pK\n", inode->i_private); file->private_data = inode->i_private; return 0; } -static int publish_unreleased_reference(struct msm_vidc_inst *inst) +static int publish_unreleased_reference(struct msm_vidc_inst *inst, + char **dbuf, char *end) { + char *cur = *dbuf; struct buffer_info *temp = NULL; if (!inst) { @@ -260,130 +277,228 @@ static int publish_unreleased_reference(struct msm_vidc_inst *inst) } if (inst->buffer_mode_set[CAPTURE_PORT] == HAL_BUFFER_MODE_DYNAMIC) { - write_str(&dbg_buf, "Pending buffer references:\n"); + cur += write_str(cur, end - cur, "Pending buffer references\n"); mutex_lock(&inst->registeredbufs.lock); list_for_each_entry(temp, &inst->registeredbufs.list, list) { if (temp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && !temp->inactive && atomic_read(&temp->ref_count)) { - write_str(&dbg_buf, - "\tpending buffer: %#lx fd[0] = %d ref_count = %d held by: %s\n", - temp->device_addr[0], - temp->fd[0], - atomic_read(&temp->ref_count), - DYNAMIC_BUF_OWNER(temp)); + cur += write_str(cur, end - cur, + "\tpending buffer: %#lx fd[0] = %d ref_count = %d held by: %s\n", + temp->device_addr[0], + temp->fd[0], + atomic_read(&temp->ref_count), + DYNAMIC_BUF_OWNER(temp)); } } mutex_unlock(&inst->registeredbufs.lock); } + + *dbuf = cur; return 0; } +static void put_inst_helper(struct kref *kref) +{ + struct msm_vidc_inst *inst = container_of(kref, + struct msm_vidc_inst, kref); + + msm_vidc_destroy(inst); +} + static ssize_t inst_info_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct msm_vidc_inst *inst = file->private_data; + struct core_inst_pair *idata = file->private_data; + struct msm_vidc_core *core; + struct msm_vidc_inst *inst, *temp = NULL; + char *dbuf, *cur, *end; int i, j; ssize_t len = 0; + if (!idata || !idata->core || !idata->inst) { + dprintk(VIDC_ERR, "%s: Invalid params\n", __func__); + return 0; + } + + core = idata->core; + inst = idata->inst; + + mutex_lock(&core->lock); + list_for_each_entry(temp, &core->instances, list) { + if (temp == inst) + break; + } + inst = ((temp == inst) && kref_get_unless_zero(&inst->kref)) ? + inst : NULL; + mutex_unlock(&core->lock); + if (!inst) { - dprintk(VIDC_ERR, "Invalid params, inst %pK\n", inst); + dprintk(VIDC_ERR, "%s: Instance has become obsolete", __func__); return 0; } - mutex_lock(&dbg_buf.lock); - INIT_DBG_BUF(dbg_buf); - write_str(&dbg_buf, "===============================\n"); - write_str(&dbg_buf, "INSTANCE: %pK (%s)\n", inst, + dbuf = kzalloc(MAX_DBG_BUF_SIZE, GFP_KERNEL); + if (!dbuf) { + dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__); + len = -ENOMEM; + goto failed_alloc; + } + cur = dbuf; + end = cur + MAX_DBG_BUF_SIZE; + + cur += write_str(cur, end - cur, "==============================\n"); + cur += write_str(cur, end - cur, "INSTANCE: %pK (%s)\n", inst, inst->session_type == MSM_VIDC_ENCODER ? "Encoder" : "Decoder"); - write_str(&dbg_buf, "===============================\n"); - write_str(&dbg_buf, "core: %pK\n", inst->core); - write_str(&dbg_buf, "height: %d\n", inst->prop.height[CAPTURE_PORT]); - write_str(&dbg_buf, "width: %d\n", inst->prop.width[CAPTURE_PORT]); - write_str(&dbg_buf, "fps: %d\n", inst->prop.fps); - write_str(&dbg_buf, "state: %d\n", inst->state); - write_str(&dbg_buf, "secure: %d\n", !!(inst->flags & VIDC_SECURE)); - write_str(&dbg_buf, "-----------Formats-------------\n"); + cur += write_str(cur, end - cur, "==============================\n"); + cur += write_str(cur, end - cur, "core: %pK\n", inst->core); + cur += write_str(cur, end - cur, "height: %d\n", + inst->prop.height[CAPTURE_PORT]); + cur += write_str(cur, end - cur, "width: %d\n", + inst->prop.width[CAPTURE_PORT]); + cur += write_str(cur, end - cur, "fps: %d\n", inst->prop.fps); + cur += write_str(cur, end - cur, "state: %d\n", inst->state); + cur += write_str(cur, end - cur, "secure: %d\n", + !!(inst->flags & VIDC_SECURE)); + cur += write_str(cur, end - cur, "-----------Formats-------------\n"); for (i = 0; i < MAX_PORT_NUM; i++) { - write_str(&dbg_buf, "capability: %s\n", i == OUTPUT_PORT ? + cur += write_str(cur, end - cur, "capability: %s\n", + i == OUTPUT_PORT ? "Output" : "Capture"); + cur += write_str(cur, end - cur, "name : %s\n", + inst->fmts[i].name); + cur += write_str(cur, end - cur, "planes : %d\n", + inst->prop.num_planes[i]); + cur += write_str(cur, end - cur, + "type: %s\n", inst->fmts[i].type == OUTPUT_PORT ? "Output" : "Capture"); - write_str(&dbg_buf, "name : %s\n", inst->fmts[i].name); - write_str(&dbg_buf, "planes : %d\n", inst->prop.num_planes[i]); - write_str( - &dbg_buf, "type: %s\n", inst->fmts[i].type == OUTPUT_PORT ? - "Output" : "Capture"); switch (inst->buffer_mode_set[i]) { case HAL_BUFFER_MODE_STATIC: - write_str(&dbg_buf, "buffer mode : %s\n", "static"); + cur += write_str(cur, end - cur, + "buffer mode : %s\n", "static"); break; case HAL_BUFFER_MODE_RING: - write_str(&dbg_buf, "buffer mode : %s\n", "ring"); + cur += write_str(cur, end - cur, + "buffer mode : %s\n", "ring"); break; case HAL_BUFFER_MODE_DYNAMIC: - write_str(&dbg_buf, "buffer mode : %s\n", "dynamic"); + cur += write_str(cur, end - cur, + "buffer mode : %s\n", "dynamic"); break; default: - write_str(&dbg_buf, "buffer mode : unsupported\n"); + cur += write_str(cur, end - cur, + "buffer mode : unsupported\n"); } - write_str(&dbg_buf, "count: %u\n", + cur += write_str(cur, end - cur, "count: %u\n", inst->bufq[i].vb2_bufq.num_buffers); for (j = 0; j < inst->prop.num_planes[i]; j++) - write_str(&dbg_buf, "size for plane %d: %u\n", j, + cur += write_str(cur, end - cur, + "size for plane %d: %u\n", j, inst->bufq[i].vb2_bufq.plane_sizes[j]); if (i < MAX_PORT_NUM - 1) - write_str(&dbg_buf, "\n"); + cur += write_str(cur, end - cur, "\n"); } - write_str(&dbg_buf, "-------------------------------\n"); + cur += write_str(cur, end - cur, "-------------------------------\n"); for (i = SESSION_MSG_START; i < SESSION_MSG_END; i++) { - write_str(&dbg_buf, "completions[%d]: %s\n", i, + cur += write_str(cur, end - cur, "completions[%d]: %s\n", i, completion_done(&inst->completions[SESSION_MSG_INDEX(i)]) ? "pending" : "done"); } - write_str(&dbg_buf, "ETB Count: %d\n", inst->count.etb); - write_str(&dbg_buf, "EBD Count: %d\n", inst->count.ebd); - write_str(&dbg_buf, "FTB Count: %d\n", inst->count.ftb); - write_str(&dbg_buf, "FBD Count: %d\n", inst->count.fbd); - - publish_unreleased_reference(inst); + cur += write_str(cur, end - cur, "ETB Count: %d\n", inst->count.etb); + cur += write_str(cur, end - cur, "EBD Count: %d\n", inst->count.ebd); + cur += write_str(cur, end - cur, "FTB Count: %d\n", inst->count.ftb); + cur += write_str(cur, end - cur, "FBD Count: %d\n", inst->count.fbd); + publish_unreleased_reference(inst, &cur, end); len = simple_read_from_buffer(buf, count, ppos, - dbg_buf.ptr, dbg_buf.filled_size); - mutex_unlock(&dbg_buf.lock); + dbuf, cur - dbuf); + + kfree(dbuf); +failed_alloc: + kref_put(&inst->kref, put_inst_helper); return len; } +static int inst_info_release(struct inode *inode, struct file *file) +{ + dprintk(VIDC_INFO, "Release inode ptr: %pK\n", inode->i_private); + file->private_data = NULL; + return 0; +} + static const struct file_operations inst_info_fops = { .open = inst_info_open, .read = inst_info_read, + .release = inst_info_release, }; struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst, struct dentry *parent) { - struct dentry *dir = NULL; + struct dentry *dir = NULL, *info = NULL; char debugfs_name[MAX_DEBUGFS_NAME]; + struct core_inst_pair *idata = NULL; + if (!inst) { dprintk(VIDC_ERR, "Invalid params, inst: %pK\n", inst); - goto failed_create_dir; + goto exit; } snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%p", inst); + + idata = kzalloc(sizeof(struct core_inst_pair), GFP_KERNEL); + if (!idata) { + dprintk(VIDC_ERR, "%s: Allocation failed!\n", __func__); + goto exit; + } + + idata->core = inst->core; + idata->inst = inst; + dir = debugfs_create_dir(debugfs_name, parent); if (!dir) { dprintk(VIDC_ERR, "Failed to create debugfs for msm_vidc\n"); goto failed_create_dir; } - if (!debugfs_create_file("info", S_IRUGO, dir, inst, &inst_info_fops)) { + + info = debugfs_create_file("info", S_IRUGO, dir, + idata, &inst_info_fops); + if (!info) { dprintk(VIDC_ERR, "debugfs_create_file: fail\n"); - goto failed_create_dir; + goto failed_create_file; } + + dir->d_inode->i_private = info->d_inode->i_private; inst->debug.pdata[FRAME_PROCESSING].sampling = true; + return dir; + +failed_create_file: + debugfs_remove_recursive(dir); + dir = NULL; failed_create_dir: + kfree(idata); +exit: return dir; } +void msm_vidc_debugfs_deinit_inst(struct msm_vidc_inst *inst) +{ + struct dentry *dentry = NULL; + + if (!inst || !inst->debugfs_root) + return; + + dentry = inst->debugfs_root; + if (dentry->d_inode) { + dprintk(VIDC_INFO, "Destroy %pK\n", dentry->d_inode->i_private); + kfree(dentry->d_inode->i_private); + dentry->d_inode->i_private = NULL; + } + debugfs_remove_recursive(dentry); + inst->debugfs_root = NULL; +} + void msm_vidc_debugfs_update(struct msm_vidc_inst *inst, enum msm_vidc_debugfs_event e) { @@ -433,8 +548,3 @@ void msm_vidc_debugfs_update(struct msm_vidc_inst *inst, } } -void msm_vidc_debugfs_deinit_drv(void) -{ - mutex_destroy(&dbg_buf.lock); -} - diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.h b/drivers/media/platform/msm/vidc/msm_vidc_debug.h index 853ce4b89f2b..95b2a6d60936 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_debug.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.h @@ -124,9 +124,9 @@ struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core, struct dentry *parent); struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst, struct dentry *parent); +void msm_vidc_debugfs_deinit_inst(struct msm_vidc_inst *inst); void msm_vidc_debugfs_update(struct msm_vidc_inst *inst, enum msm_vidc_debugfs_event e); -void msm_vidc_debugfs_deinit_drv(void); static inline void tic(struct msm_vidc_inst *i, enum profiling_points p, char *b) diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h index 690a61f4824f..4cb900bbca10 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h +++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h @@ -355,7 +355,7 @@ struct buffer_info *device_to_uvaddr(struct msm_vidc_list *buf_list, int buf_ref_get(struct msm_vidc_inst *inst, struct buffer_info *binfo); int buf_ref_put(struct msm_vidc_inst *inst, struct buffer_info *binfo); int output_buffer_cache_invalidate(struct msm_vidc_inst *inst, - struct buffer_info *binfo); + struct buffer_info *binfo, struct v4l2_buffer *b); int qbuf_dynamic_buf(struct msm_vidc_inst *inst, struct buffer_info *binfo); int unmap_and_deregister_buf(struct msm_vidc_inst *inst, @@ -369,7 +369,7 @@ struct msm_smem *msm_smem_alloc(void *clt, size_t size, u32 align, u32 flags, void msm_smem_free(void *clt, struct msm_smem *mem); void msm_smem_delete_client(void *clt); int msm_smem_cache_operations(void *clt, struct msm_smem *mem, - enum smem_cache_ops); + enum smem_cache_ops, int size); struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset, enum hal_buffer buffer_type); struct context_bank_info *msm_smem_get_context_bank(void *clt, diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c index 4cc977e568ee..59783dc87e5b 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c @@ -1137,8 +1137,11 @@ int read_platform_resources_from_dt( "qcom,max-secure-instances", &res->max_secure_inst_count); - res->cx_ipeak_context = cx_ipeak_register(pdev->dev.of_node, - "qcom,cx-ipeak-data"); + if (of_find_property(pdev->dev.of_node, + "qcom,cx-ipeak-data", NULL)) { + res->cx_ipeak_context = cx_ipeak_register( + pdev->dev.of_node, "qcom,cx-ipeak-data"); + } if (IS_ERR(res->cx_ipeak_context)) { rc = PTR_ERR(res->cx_ipeak_context); diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c index bc72c4a56c91..52b56f615da9 100644 --- a/drivers/media/platform/msm/vidc/venus_hfi.c +++ b/drivers/media/platform/msm/vidc/venus_hfi.c @@ -582,7 +582,7 @@ static int __smem_alloc(struct venus_hfi_device *dev, dprintk(VIDC_DBG, "__smem_alloc: ptr = %pK, size = %d\n", alloc->kvaddr, size); rc = msm_smem_cache_operations(dev->hal_client, alloc, - SMEM_CACHE_CLEAN); + SMEM_CACHE_CLEAN, -1); if (rc) { dprintk(VIDC_WARN, "Failed to clean cache\n"); dprintk(VIDC_WARN, "This may result in undefined behavior\n"); diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h index 820c8685a75b..6cc5f9f50ba1 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -201,7 +201,7 @@ enum hal_property { HAL_PARAM_VENC_H264_ENTROPY_CABAC_MODEL, HAL_CONFIG_VENC_MAX_BITRATE, HAL_PARAM_VENC_H264_VUI_TIMING_INFO, - HAL_PARAM_VENC_H264_GENERATE_AUDNAL, + HAL_PARAM_VENC_GENERATE_AUDNAL, HAL_PARAM_VENC_MAX_NUM_B_FRAMES, HAL_PARAM_BUFFER_ALLOC_MODE, HAL_PARAM_VDEC_FRAME_ASSEMBLY, diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h index bb9958b0a819..31af06cd88ef 100644 --- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h +++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -340,7 +340,7 @@ struct hfi_buffer_info { (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x014) #define HFI_PROPERTY_PARAM_VENC_H264_PPS_ID \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x015) -#define HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL \ +#define HFI_PROPERTY_PARAM_VENC_GENERATE_AUDNAL \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x016) #define HFI_PROPERTY_PARAM_VENC_ASPECT_RATIO \ (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x017) diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index c8946f98ced4..7727789dbda1 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -173,6 +173,7 @@ static void s5p_mfc_watchdog_worker(struct work_struct *work) } s5p_mfc_clock_on(); ret = s5p_mfc_init_hw(dev); + s5p_mfc_clock_off(); if (ret) mfc_err("Failed to reinit FW\n"); } diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 2cdb740cde48..f838d9c7ed12 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -1321,8 +1321,8 @@ static int mceusb_dev_probe(struct usb_interface *intf, } } } - if (ep_in == NULL) { - dev_dbg(&intf->dev, "inbound and/or endpoint not found"); + if (!ep_in || !ep_out) { + dev_dbg(&intf->dev, "required endpoints not found\n"); return -ENODEV; } diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 317ef63ee789..8d96a22647b3 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -281,6 +281,14 @@ static void free_firmware(struct xc2028_data *priv) int i; tuner_dbg("%s called\n", __func__); + /* free allocated f/w string */ + if (priv->fname != firmware_name) + kfree(priv->fname); + priv->fname = NULL; + + priv->state = XC2028_NO_FIRMWARE; + memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); + if (!priv->firm) return; @@ -291,9 +299,6 @@ static void free_firmware(struct xc2028_data *priv) priv->firm = NULL; priv->firm_size = 0; - priv->state = XC2028_NO_FIRMWARE; - - memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); } static int load_all_firmwares(struct dvb_frontend *fe, @@ -884,9 +889,8 @@ read_not_reliable: return 0; fail: - priv->state = XC2028_NO_FIRMWARE; + free_firmware(priv); - memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); if (retry_count < 8) { msleep(50); retry_count++; @@ -1332,11 +1336,8 @@ static int xc2028_dvb_release(struct dvb_frontend *fe) mutex_lock(&xc2028_list_mutex); /* only perform final cleanup if this is the last instance */ - if (hybrid_tuner_report_instance_count(priv) == 1) { + if (hybrid_tuner_report_instance_count(priv) == 1) free_firmware(priv); - kfree(priv->ctrl.fname); - priv->ctrl.fname = NULL; - } if (priv) hybrid_tuner_release_state(priv); @@ -1399,19 +1400,8 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) /* * Copy the config data. - * For the firmware name, keep a local copy of the string, - * in order to avoid troubles during device release. */ - kfree(priv->ctrl.fname); - priv->ctrl.fname = NULL; memcpy(&priv->ctrl, p, sizeof(priv->ctrl)); - if (p->fname) { - priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL); - if (priv->ctrl.fname == NULL) { - rc = -ENOMEM; - goto unlock; - } - } /* * If firmware name changed, frees firmware. As free_firmware will @@ -1426,10 +1416,15 @@ static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg) if (priv->state == XC2028_NO_FIRMWARE) { if (!firmware_name[0]) - priv->fname = priv->ctrl.fname; + priv->fname = kstrdup(p->fname, GFP_KERNEL); else priv->fname = firmware_name; + if (!priv->fname) { + rc = -ENOMEM; + goto unlock; + } + rc = request_firmware_nowait(THIS_MODULE, 1, priv->fname, priv->i2c_props.adap->dev.parent, diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c index de4ae5eb4830..10d8a08e36e6 100644 --- a/drivers/media/usb/cx231xx/cx231xx-audio.c +++ b/drivers/media/usb/cx231xx/cx231xx-audio.c @@ -671,10 +671,8 @@ static int cx231xx_audio_init(struct cx231xx *dev) spin_lock_init(&adev->slock); err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto err_free_card; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx231xx_pcm_capture); @@ -688,10 +686,9 @@ static int cx231xx_audio_init(struct cx231xx *dev) INIT_WORK(&dev->wq_trigger, audio_trigger); err = snd_card_register(card); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto err_free_card; + adev->sndcard = card; adev->udev = dev->udev; @@ -701,6 +698,11 @@ static int cx231xx_audio_init(struct cx231xx *dev) hs_config_info[0].interface_info. audio_index + 1]; + if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) { + err = -ENODEV; + goto err_free_card; + } + adev->end_point_addr = uif->altsetting[0].endpoint[isoc_pipe].desc. bEndpointAddress; @@ -710,13 +712,20 @@ static int cx231xx_audio_init(struct cx231xx *dev) "audio EndPoint Addr 0x%x, Alternate settings: %i\n", adev->end_point_addr, adev->num_alt); adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL); - - if (adev->alt_max_pkt_size == NULL) - return -ENOMEM; + if (!adev->alt_max_pkt_size) { + err = -ENOMEM; + goto err_free_card; + } for (i = 0; i < adev->num_alt; i++) { - u16 tmp = - le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc. + u16 tmp; + + if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) { + err = -ENODEV; + goto err_free_pkt_size; + } + + tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc. wMaxPacketSize); adev->alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); @@ -726,6 +735,13 @@ static int cx231xx_audio_init(struct cx231xx *dev) } return 0; + +err_free_pkt_size: + kfree(adev->alt_max_pkt_size); +err_free_card: + snd_card_free(card); + + return err; } static int cx231xx_audio_fini(struct cx231xx *dev) diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index 8389c162bc89..2c5f76d588ac 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1447,6 +1447,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, uif = udev->actconfig->interface[idx]; + if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) + return -ENODEV; + dev->video_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe].desc.bEndpointAddress; dev->video_mode.num_alt = uif->num_altsetting; @@ -1460,7 +1463,12 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, return -ENOMEM; for (i = 0; i < dev->video_mode.num_alt; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize); + u16 tmp; + + if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) + return -ENODEV; + + tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize); dev->video_mode.alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); dev_dbg(dev->dev, "Alternate setting %i, max size= %i\n", i, @@ -1477,6 +1485,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, } uif = udev->actconfig->interface[idx]; + if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) + return -ENODEV; + dev->vbi_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe].desc. bEndpointAddress; @@ -1493,8 +1504,12 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, return -ENOMEM; for (i = 0; i < dev->vbi_mode.num_alt; i++) { - u16 tmp = - le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + u16 tmp; + + if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) + return -ENODEV; + + tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. desc.wMaxPacketSize); dev->vbi_mode.alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); @@ -1514,6 +1529,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, } uif = udev->actconfig->interface[idx]; + if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) + return -ENODEV; + dev->sliced_cc_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe].desc. bEndpointAddress; @@ -1528,7 +1546,12 @@ static int cx231xx_init_v4l2(struct cx231xx *dev, return -ENOMEM; for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + u16 tmp; + + if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) + return -ENODEV; + + tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. desc.wMaxPacketSize); dev->sliced_cc_mode.alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); @@ -1693,6 +1716,11 @@ static int cx231xx_usb_probe(struct usb_interface *interface, } uif = udev->actconfig->interface[idx]; + if (uif->altsetting[0].desc.bNumEndpoints < isoc_pipe + 1) { + retval = -ENODEV; + goto err_video_alt; + } + dev->ts1_mode.end_point_addr = uif->altsetting[0].endpoint[isoc_pipe]. desc.bEndpointAddress; @@ -1710,7 +1738,14 @@ static int cx231xx_usb_probe(struct usb_interface *interface, } for (i = 0; i < dev->ts1_mode.num_alt; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i]. + u16 tmp; + + if (uif->altsetting[i].desc.bNumEndpoints < isoc_pipe + 1) { + retval = -ENODEV; + goto err_video_alt; + } + + tmp = le16_to_cpu(uif->altsetting[i]. endpoint[isoc_pipe].desc. wMaxPacketSize); dev->ts1_mode.alt_max_pkt_size[i] = diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index ab58f0b9da5c..d1b4b729e814 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -783,6 +783,9 @@ int dib0700_rc_setup(struct dvb_usb_device *d, struct usb_interface *intf) /* Starting in firmware 1.20, the RC info is provided on a bulk pipe */ + if (intf->altsetting[0].desc.bNumEndpoints < rc_ep + 1) + return -ENODEV; + purb = usb_alloc_urb(0, GFP_KERNEL); if (purb == NULL) { err("rc usb alloc urb failed"); diff --git a/drivers/media/usb/dvb-usb/ttusb2.c b/drivers/media/usb/dvb-usb/ttusb2.c index f10717311e05..dd93c2c8fea9 100644 --- a/drivers/media/usb/dvb-usb/ttusb2.c +++ b/drivers/media/usb/dvb-usb/ttusb2.c @@ -78,6 +78,9 @@ static int ttusb2_msg(struct dvb_usb_device *d, u8 cmd, u8 *s, *r = NULL; int ret = 0; + if (4 + rlen > 64) + return -EIO; + s = kzalloc(wlen+4, GFP_KERNEL); if (!s) return -ENOMEM; @@ -381,6 +384,22 @@ static int ttusb2_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num write_read = i+1 < num && (msg[i+1].flags & I2C_M_RD); read = msg[i].flags & I2C_M_RD; + if (3 + msg[i].len > sizeof(obuf)) { + err("i2c wr len=%d too high", msg[i].len); + break; + } + if (write_read) { + if (3 + msg[i+1].len > sizeof(ibuf)) { + err("i2c rd len=%d too high", msg[i+1].len); + break; + } + } else if (read) { + if (3 + msg[i].len > sizeof(ibuf)) { + err("i2c rd len=%d too high", msg[i].len); + break; + } + } + obuf[0] = (msg[i].addr << 1) | (write_read | read); if (read) obuf[1] = 0; diff --git a/drivers/media/usb/gspca/konica.c b/drivers/media/usb/gspca/konica.c index 0712b1bc90b4..0f6d57fbf91b 100644 --- a/drivers/media/usb/gspca/konica.c +++ b/drivers/media/usb/gspca/konica.c @@ -188,6 +188,9 @@ static int sd_start(struct gspca_dev *gspca_dev) return -EIO; } + if (alt->desc.bNumEndpoints < 2) + return -ENODEV; + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); n = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c index e1907cd0c3b7..7613d1fee104 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c @@ -123,15 +123,10 @@ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) memset(&tvdata,0,sizeof(tvdata)); eeprom = pvr2_eeprom_fetch(hdw); - if (!eeprom) return -EINVAL; - - { - struct i2c_client fake_client; - /* Newer version expects a useless client interface */ - fake_client.addr = hdw->eeprom_addr; - fake_client.adapter = &hdw->i2c_adap; - tveeprom_hauppauge_analog(&fake_client,&tvdata,eeprom); - } + if (!eeprom) + return -EINVAL; + + tveeprom_hauppauge_analog(NULL, &tvdata, eeprom); trace_eeprom("eeprom assumed v4l tveeprom module"); trace_eeprom("eeprom direct call results:"); diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index d1dc1a198e3e..91d709efef7a 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -1523,7 +1523,14 @@ static int usbvision_probe(struct usb_interface *intf, } for (i = 0; i < usbvision->num_alt; i++) { - u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc. + u16 tmp; + + if (uif->altsetting[i].desc.bNumEndpoints < 2) { + ret = -ENODEV; + goto err_pkt; + } + + tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc. wMaxPacketSize); usbvision->alt_max_pkt_size[i] = (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 7433ba5c4bad..fd6a3b36208e 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -604,6 +604,14 @@ static int zr364xx_read_video_callback(struct zr364xx_camera *cam, ptr = pdest = frm->lpvbits; if (frm->ulState == ZR364XX_READ_IDLE) { + if (purb->actual_length < 128) { + /* header incomplete */ + dev_info(&cam->udev->dev, + "%s: buffer (%d bytes) too small to hold jpeg header. Discarding.\n", + __func__, purb->actual_length); + return -EINVAL; + } + frm->ulState = ZR364XX_READ_FRAME; frm->cur_size = 0; diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 47f37683893a..3dc9ed2e0774 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -793,7 +793,7 @@ EXPORT_SYMBOL_GPL(vb2_core_create_bufs); */ void *vb2_plane_vaddr(struct vb2_buffer *vb, unsigned int plane_no) { - if (plane_no > vb->num_planes || !vb->planes[plane_no].mem_priv) + if (plane_no >= vb->num_planes || !vb->planes[plane_no].mem_priv) return NULL; return call_ptr_memop(vb, vaddr, vb->planes[plane_no].mem_priv); diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index c30290f33430..fe51e9709210 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -375,8 +375,8 @@ int omap_tll_init(struct usbhs_omap_platform_data *pdata) * and use SDR Mode */ reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE - | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE); + reg |= OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF; } else if (pdata->port_mode[i] == OMAP_EHCI_PORT_MODE_HSIC) { /* diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 2a4abf736d89..d86795bf9453 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -551,7 +551,7 @@ config VEXPRESS_SYSCFG config UID_SYS_STATS bool "Per-UID statistics" - depends on PROFILING + depends on PROFILING && TASK_XACCT && TASK_IO_ACCOUNTING help Per UID based cpu time statistics exported to /proc/uid_cputime Per UID based io statistics exported to /proc/uid_io diff --git a/drivers/misc/c2port/c2port-duramar2150.c b/drivers/misc/c2port/c2port-duramar2150.c index 5484301d57d9..3dc61ea7dc64 100644 --- a/drivers/misc/c2port/c2port-duramar2150.c +++ b/drivers/misc/c2port/c2port-duramar2150.c @@ -129,8 +129,8 @@ static int __init duramar2150_c2port_init(void) duramar2150_c2port_dev = c2port_device_register("uc", &duramar2150_c2port_ops, NULL); - if (!duramar2150_c2port_dev) { - ret = -ENODEV; + if (IS_ERR(duramar2150_c2port_dev)) { + ret = PTR_ERR(duramar2150_c2port_dev); goto free_region; } diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index 783337d22f36..10a02934bfc0 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c @@ -158,11 +158,8 @@ static long afu_ioctl_start_work(struct cxl_context *ctx, /* Do this outside the status_mutex to avoid a circular dependency with * the locking in cxl_mmap_fault() */ - if (copy_from_user(&work, uwork, - sizeof(struct cxl_ioctl_start_work))) { - rc = -EFAULT; - goto out; - } + if (copy_from_user(&work, uwork, sizeof(work))) + return -EFAULT; mutex_lock(&ctx->status_mutex); if (ctx->status != OPENED) { diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c index 33ec0c15efa6..c6f2dbfe573d 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -12,10 +12,13 @@ #define pr_fmt(fmt) "%s: " fmt, __func__ +#include <linux/platform_device.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/fs.h> +#include <linux/file.h> +#include <linux/uaccess.h> #include <linux/cdev.h> #include <linux/sched.h> #include <linux/list.h> @@ -30,6 +33,8 @@ #include <linux/errno.h> #include <linux/hdcp_qseecom.h> #include <linux/kthread.h> +#include <linux/of.h> +#include <video/msm_hdmi_hdcp_mgr.h> #include "qseecom_kernel.h" @@ -542,6 +547,24 @@ struct hdcp_lib_message_map { const char *msg_name; }; +struct msm_hdcp_mgr { + struct platform_device *pdev; + dev_t dev_num; + struct cdev cdev; + struct class *class; + struct device *device; + struct HDCP_V2V1_MSG_TOPOLOGY cached_tp; + u32 tp_msgid; + void *client_ctx; + struct hdcp_lib_handle *handle; +}; + +#define CLASS_NAME "hdcp" +#define DRIVER_NAME "msm_hdcp" + +static struct msm_hdcp_mgr *hdcp_drv_mgr; +static struct hdcp_lib_handle *drv_client_handle; + static void hdcp_lib_clean(struct hdcp_lib_handle *handle); static void hdcp_lib_init(struct hdcp_lib_handle *handle); static void hdcp_lib_msg_sent(struct hdcp_lib_handle *handle); @@ -2288,7 +2311,7 @@ int hdcp1_set_enc(bool enable) } if (hdcp1_enc_enabled == enable) { - pr_debug("already %s\n", enable ? "enabled" : "disabled"); + pr_info("already %s\n", enable ? "enabled" : "disabled"); goto end; } @@ -2318,7 +2341,7 @@ int hdcp1_set_enc(bool enable) } hdcp1_enc_enabled = enable; - pr_debug("%s success\n", enable ? "enable" : "disable"); + pr_info("%s success\n", enable ? "enable" : "disable"); end: mutex_unlock(&hdcp1_ta_cmd_lock); return rc; @@ -2393,7 +2416,13 @@ int hdcp_library_register(struct hdcp_register_data *data) } *data->hdcp_ctx = handle; + /* Cache the client ctx to be used later + * HDCP driver probe happens earlier than + * SDE driver probe hence caching it to + * be used later. + */ + drv_client_handle = handle; handle->thread = kthread_run(kthread_worker_fn, &handle->worker, "hdcp_tz_lib"); @@ -2433,3 +2462,273 @@ void hdcp_library_deregister(void *phdcpcontext) kzfree(handle); } EXPORT_SYMBOL(hdcp_library_deregister); + +void hdcp1_notify_topology(void) +{ + char *envp[4]; + char *a; + char *b; + + a = kzalloc(SZ_16, GFP_KERNEL); + + if (!a) + return; + + b = kzalloc(SZ_16, GFP_KERNEL); + + if (!b) { + kfree(a); + return; + } + + envp[0] = "HDCP_MGR_EVENT=MSG_READY"; + envp[1] = a; + envp[2] = b; + envp[3] = NULL; + + snprintf(envp[1], 16, "%d", (int)DOWN_CHECK_TOPOLOGY); + snprintf(envp[2], 16, "%d", (int)HDCP_V1_TX); + + kobject_uevent_env(&hdcp_drv_mgr->device->kobj, KOBJ_CHANGE, envp); + kfree(a); + kfree(b); +} + +static ssize_t msm_hdcp_1x_sysfs_rda_tp(struct device *dev, +struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + if (!hdcp_drv_mgr) { + pr_err("invalid input\n"); + return -EINVAL; + } + + switch (hdcp_drv_mgr->tp_msgid) { + case DOWN_CHECK_TOPOLOGY: + case DOWN_REQUEST_TOPOLOGY: + buf[MSG_ID_IDX] = hdcp_drv_mgr->tp_msgid; + buf[RET_CODE_IDX] = HDCP_AUTHED; + ret = HEADER_LEN; + + memcpy(buf + HEADER_LEN, &hdcp_drv_mgr->cached_tp, + sizeof(struct HDCP_V2V1_MSG_TOPOLOGY)); + + ret += sizeof(struct HDCP_V2V1_MSG_TOPOLOGY); + + /* clear the flag once data is read back to user space*/ + hdcp_drv_mgr->tp_msgid = -1; + break; + default: + ret = -EINVAL; + } + + return ret; +} /* hdcp_1x_sysfs_rda_tp*/ + +static ssize_t msm_hdcp_1x_sysfs_wta_tp(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int msgid = 0; + ssize_t ret = count; + + if (!hdcp_drv_mgr || !buf) { + pr_err("invalid input\n"); + return -EINVAL; + } + + msgid = buf[0]; + + switch (msgid) { + case DOWN_CHECK_TOPOLOGY: + case DOWN_REQUEST_TOPOLOGY: + hdcp_drv_mgr->tp_msgid = msgid; + break; + /* more cases added here */ + default: + ret = -EINVAL; + } + + return ret; +} /* hdmi_tx_sysfs_wta_hpd */ + +static ssize_t hdmi_hdcp2p2_sysfs_wta_min_level_change(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc; + int min_enc_lvl; + struct hdcp_lib_handle *handle; + ssize_t ret = count; + + handle = hdcp_drv_mgr->handle; + + rc = kstrtoint(buf, 10, &min_enc_lvl); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + return -EINVAL; + } + + if (handle && handle->client_ops->notify_lvl_change) { + handle->client_ops->notify_lvl_change(handle->client_ctx, + min_enc_lvl); + } + + return ret; +} + +static DEVICE_ATTR(tp, S_IRUGO | S_IWUSR, msm_hdcp_1x_sysfs_rda_tp, +msm_hdcp_1x_sysfs_wta_tp); + +static DEVICE_ATTR(min_level_change, S_IWUSR, NULL, +hdmi_hdcp2p2_sysfs_wta_min_level_change); + +void hdcp1_cache_repeater_topology(void *hdcp1_cached_tp) +{ + memcpy((void *)&hdcp_drv_mgr->cached_tp, + hdcp1_cached_tp, + sizeof(struct HDCP_V2V1_MSG_TOPOLOGY)); +} + +static struct attribute *msm_hdcp_fs_attrs[] = { + &dev_attr_tp.attr, + &dev_attr_min_level_change.attr, + NULL +}; + +static struct attribute_group msm_hdcp_fs_attr_group = { + .attrs = msm_hdcp_fs_attrs +}; + +static int msm_hdcp_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int msm_hdcp_close(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations msm_hdcp_fops = { + .owner = THIS_MODULE, + .open = msm_hdcp_open, + .release = msm_hdcp_close, +}; + +static const struct of_device_id msm_hdcp_dt_match[] = { + { .compatible = "qcom,msm-hdcp",}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_hdcp_dt_match); + +static int msm_hdcp_probe(struct platform_device *pdev) +{ + int ret; + + hdcp_drv_mgr = devm_kzalloc(&pdev->dev, sizeof(struct msm_hdcp_mgr), + GFP_KERNEL); + if (!hdcp_drv_mgr) + return -ENOMEM; + + hdcp_drv_mgr->pdev = pdev; + + platform_set_drvdata(pdev, hdcp_drv_mgr); + + ret = alloc_chrdev_region(&hdcp_drv_mgr->dev_num, 0, 1, DRIVER_NAME); + if (ret < 0) { + pr_err("alloc_chrdev_region failed ret = %d\n", ret); + goto error_get_dev_num; + } + + hdcp_drv_mgr->class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(hdcp_drv_mgr->class)) { + ret = PTR_ERR(hdcp_drv_mgr->class); + pr_err("couldn't create class rc = %d\n", ret); + goto error_class_create; + } + + hdcp_drv_mgr->device = device_create(hdcp_drv_mgr->class, NULL, + hdcp_drv_mgr->dev_num, NULL, DRIVER_NAME); + if (IS_ERR(hdcp_drv_mgr->device)) { + ret = PTR_ERR(hdcp_drv_mgr->device); + pr_err("device_create failed %d\n", ret); + goto error_class_device_create; + } + + cdev_init(&hdcp_drv_mgr->cdev, &msm_hdcp_fops); + ret = cdev_add(&hdcp_drv_mgr->cdev, + MKDEV(MAJOR(hdcp_drv_mgr->dev_num), 0), 1); + if (ret < 0) { + pr_err("cdev_add failed %d\n", ret); + goto error_cdev_add; + } + + ret = sysfs_create_group(&hdcp_drv_mgr->device->kobj, + &msm_hdcp_fs_attr_group); + if (ret) + pr_err("unable to register rotator sysfs nodes\n"); + + /* Store the handle in the hdcp drv mgr + * to be used for the sysfs notifications + */ + hdcp_drv_mgr->handle = drv_client_handle; + + return 0; +error_cdev_add: + device_destroy(hdcp_drv_mgr->class, hdcp_drv_mgr->dev_num); +error_class_device_create: + class_destroy(hdcp_drv_mgr->class); +error_class_create: + unregister_chrdev_region(hdcp_drv_mgr->dev_num, 1); +error_get_dev_num: + devm_kfree(&pdev->dev, hdcp_drv_mgr); + hdcp_drv_mgr = NULL; + return ret; +} + +static int msm_hdcp_remove(struct platform_device *pdev) +{ + struct msm_hdcp_mgr *mgr; + + mgr = (struct msm_hdcp_mgr *)platform_get_drvdata(pdev); + if (!mgr) + return -ENODEV; + + sysfs_remove_group(&hdcp_drv_mgr->device->kobj, + &msm_hdcp_fs_attr_group); + cdev_del(&hdcp_drv_mgr->cdev); + device_destroy(hdcp_drv_mgr->class, hdcp_drv_mgr->dev_num); + class_destroy(hdcp_drv_mgr->class); + unregister_chrdev_region(hdcp_drv_mgr->dev_num, 1); + + devm_kfree(&pdev->dev, hdcp_drv_mgr); + hdcp_drv_mgr = NULL; + return 0; +} + +static struct platform_driver msm_hdcp_driver = { + .probe = msm_hdcp_probe, + .remove = msm_hdcp_remove, + .driver = { + .name = "msm_hdcp", + .of_match_table = msm_hdcp_dt_match, + .pm = NULL, + } +}; + +static int __init msm_hdcp_init(void) +{ + return platform_driver_register(&msm_hdcp_driver); +} + +static void __exit msm_hdcp_exit(void) +{ + return platform_driver_unregister(&msm_hdcp_driver); +} + +module_init(msm_hdcp_init); +module_exit(msm_hdcp_exit); + +MODULE_DESCRIPTION("MSM HDCP driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c index 5419bd1655c1..b292ea70fb40 100644 --- a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c +++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c @@ -26,11 +26,14 @@ #include <linux/debugfs.h> #include <linux/msm_audio_ion.h> #include <linux/compat.h> +#include <linux/mutex.h> #include "audio_utils_aio.h" #ifdef CONFIG_USE_DEV_CTRL_VOLUME #include <linux/qdsp6v2/audio_dev_ctl.h> #endif /*CONFIG_USE_DEV_CTRL_VOLUME*/ +static DEFINE_MUTEX(lock); #ifdef CONFIG_DEBUG_FS + int audio_aio_debug_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; @@ -43,29 +46,37 @@ ssize_t audio_aio_debug_read(struct file *file, char __user *buf, const int debug_bufmax = 4096; static char buffer[4096]; int n = 0; - struct q6audio_aio *audio = file->private_data; + struct q6audio_aio *audio; - mutex_lock(&audio->lock); - n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened); - n += scnprintf(buffer + n, debug_bufmax - n, - "enabled %d\n", audio->enabled); - n += scnprintf(buffer + n, debug_bufmax - n, - "stopped %d\n", audio->stopped); - n += scnprintf(buffer + n, debug_bufmax - n, - "feedback %d\n", audio->feedback); - mutex_unlock(&audio->lock); - /* Following variables are only useful for debugging when - * when playback halts unexpectedly. Thus, no mutual exclusion - * enforced - */ - n += scnprintf(buffer + n, debug_bufmax - n, - "wflush %d\n", audio->wflush); - n += scnprintf(buffer + n, debug_bufmax - n, - "rflush %d\n", audio->rflush); - n += scnprintf(buffer + n, debug_bufmax - n, - "inqueue empty %d\n", list_empty(&audio->in_queue)); - n += scnprintf(buffer + n, debug_bufmax - n, - "outqueue empty %d\n", list_empty(&audio->out_queue)); + mutex_lock(&lock); + if (file->private_data != NULL) { + audio = file->private_data; + mutex_lock(&audio->lock); + n = scnprintf(buffer, debug_bufmax, "opened %d\n", + audio->opened); + n += scnprintf(buffer + n, debug_bufmax - n, + "enabled %d\n", audio->enabled); + n += scnprintf(buffer + n, debug_bufmax - n, + "stopped %d\n", audio->stopped); + n += scnprintf(buffer + n, debug_bufmax - n, + "feedback %d\n", audio->feedback); + mutex_unlock(&audio->lock); + /* Following variables are only useful for debugging when + * when playback halts unexpectedly. Thus, no mutual exclusion + * enforced + */ + n += scnprintf(buffer + n, debug_bufmax - n, + "wflush %d\n", audio->wflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "rflush %d\n", audio->rflush); + n += scnprintf(buffer + n, debug_bufmax - n, + "inqueue empty %d\n", + list_empty(&audio->in_queue)); + n += scnprintf(buffer + n, debug_bufmax - n, + "outqueue empty %d\n", + list_empty(&audio->out_queue)); + } + mutex_unlock(&lock); buffer[n] = 0; return simple_read_from_buffer(buf, count, ppos, buffer, n); } @@ -573,6 +584,7 @@ int audio_aio_release(struct inode *inode, struct file *file) { struct q6audio_aio *audio = file->private_data; pr_debug("%s[%pK]\n", __func__, audio); + mutex_lock(&lock); mutex_lock(&audio->lock); mutex_lock(&audio->read_lock); mutex_lock(&audio->write_lock); @@ -616,6 +628,8 @@ int audio_aio_release(struct inode *inode, struct file *file) #endif kfree(audio->codec_cfg); kfree(audio); + file->private_data = NULL; + mutex_unlock(&lock); return 0; } diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 1c7cc917faa6..7cdcd69cecf4 100644 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -1257,7 +1257,7 @@ static int qseecom_unregister_listener(struct qseecom_dev_handle *data) atomic_read(&data->ioctl_count) <= 1)) { pr_err("Interrupted from abort\n"); ret = -ERESTARTSYS; - break; + return ret; } } @@ -2908,7 +2908,11 @@ static int qseecom_send_service_cmd(struct qseecom_dev_handle *data, } if (req.cmd_id == QSEOS_RPMB_CHECK_PROV_STATUS_COMMAND) { pr_warn("RPMB key status is 0x%x\n", resp.result); - *(uint32_t *)req.resp_buf = resp.result; + if (put_user(resp.result, + (uint32_t __user *)req.resp_buf)) { + ret = -EINVAL; + goto exit; + } ret = 0; } break; @@ -4375,9 +4379,9 @@ int qseecom_start_app(struct qseecom_handle **handle, return -EINVAL; } - if (strlen(app_name) >= MAX_APP_NAME_SIZE) { + if (strnlen(app_name, MAX_APP_NAME_SIZE) == MAX_APP_NAME_SIZE) { pr_err("The app_name (%s) with length %zu is not valid\n", - app_name, strlen(app_name)); + app_name, strnlen(app_name, MAX_APP_NAME_SIZE)); return -EINVAL; } @@ -6498,11 +6502,16 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data, void *cmd_buf = NULL; size_t cmd_len; struct sglist_info *table = data->sglistinfo_ptr; + void *req_ptr = NULL; + void *resp_ptr = NULL; ret = __qseecom_qteec_validate_msg(data, req); if (ret) return ret; + req_ptr = req->req_ptr; + resp_ptr = req->resp_ptr; + /* find app_id & img_name from list */ spin_lock_irqsave(&qseecom.registered_app_list_lock, flags); list_for_each_entry(ptr_app, &qseecom.registered_app_list_head, @@ -6520,6 +6529,11 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data, return -ENOENT; } + req->req_ptr = (void *)__qseecom_uvirt_to_kvirt(data, + (uintptr_t)req->req_ptr); + req->resp_ptr = (void *)__qseecom_uvirt_to_kvirt(data, + (uintptr_t)req->resp_ptr); + if ((cmd_id == QSEOS_TEE_OPEN_SESSION) || (cmd_id == QSEOS_TEE_REQUEST_CANCELLATION)) { ret = __qseecom_update_qteec_req_buf( @@ -6531,10 +6545,10 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data, if (qseecom.qsee_version < QSEE_VERSION_40) { ireq.app_id = data->client.app_id; ireq.req_ptr = (uint32_t)__qseecom_uvirt_to_kphys(data, - (uintptr_t)req->req_ptr); + (uintptr_t)req_ptr); ireq.req_len = req->req_len; ireq.resp_ptr = (uint32_t)__qseecom_uvirt_to_kphys(data, - (uintptr_t)req->resp_ptr); + (uintptr_t)resp_ptr); ireq.resp_len = req->resp_len; ireq.sglistinfo_ptr = (uint32_t)virt_to_phys(table); ireq.sglistinfo_len = SGLISTINFO_TABLE_SIZE; @@ -6545,10 +6559,10 @@ static int __qseecom_qteec_issue_cmd(struct qseecom_dev_handle *data, } else { ireq_64bit.app_id = data->client.app_id; ireq_64bit.req_ptr = (uint64_t)__qseecom_uvirt_to_kphys(data, - (uintptr_t)req->req_ptr); + (uintptr_t)req_ptr); ireq_64bit.req_len = req->req_len; ireq_64bit.resp_ptr = (uint64_t)__qseecom_uvirt_to_kphys(data, - (uintptr_t)req->resp_ptr); + (uintptr_t)resp_ptr); ireq_64bit.resp_len = req->resp_len; if ((data->client.app_arch == ELFCLASS32) && ((ireq_64bit.req_ptr >= diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c index ad21276c8d9e..3c9d311106cd 100644 --- a/drivers/misc/uid_sys_stats.c +++ b/drivers/misc/uid_sys_stats.c @@ -49,7 +49,8 @@ struct io_stats { #define UID_STATE_TOTAL_CURR 2 #define UID_STATE_TOTAL_LAST 3 -#define UID_STATE_SIZE 4 +#define UID_STATE_DEAD_TASKS 4 +#define UID_STATE_SIZE 5 struct uid_entry { uid_t uid; @@ -93,7 +94,7 @@ static struct uid_entry *find_or_register_uid(uid_t uid) static int uid_cputime_show(struct seq_file *m, void *v) { - struct uid_entry *uid_entry; + struct uid_entry *uid_entry = NULL; struct task_struct *task, *temp; struct user_namespace *user_ns = current_user_ns(); cputime_t utime; @@ -111,7 +112,8 @@ static int uid_cputime_show(struct seq_file *m, void *v) read_lock(&tasklist_lock); do_each_thread(temp, task) { uid = from_kuid_munged(user_ns, task_uid(task)); - uid_entry = find_or_register_uid(uid); + if (!uid_entry || uid_entry->uid != uid) + uid_entry = find_or_register_uid(uid); if (!uid_entry) { read_unlock(&tasklist_lock); rt_mutex_unlock(&uid_lock); @@ -214,35 +216,44 @@ static u64 compute_write_bytes(struct task_struct *task) return task->ioac.write_bytes - task->ioac.cancelled_write_bytes; } -static void add_uid_io_curr_stats(struct uid_entry *uid_entry, - struct task_struct *task) +static void add_uid_io_stats(struct uid_entry *uid_entry, + struct task_struct *task, int slot) { - struct io_stats *io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; + struct io_stats *io_slot = &uid_entry->io[slot]; - io_curr->read_bytes += task->ioac.read_bytes; - io_curr->write_bytes += compute_write_bytes(task); - io_curr->rchar += task->ioac.rchar; - io_curr->wchar += task->ioac.wchar; - io_curr->fsync += task->ioac.syscfs; + io_slot->read_bytes += task->ioac.read_bytes; + io_slot->write_bytes += compute_write_bytes(task); + io_slot->rchar += task->ioac.rchar; + io_slot->wchar += task->ioac.wchar; + io_slot->fsync += task->ioac.syscfs; } -static void clean_uid_io_last_stats(struct uid_entry *uid_entry, - struct task_struct *task) +static void compute_uid_io_bucket_stats(struct io_stats *io_bucket, + struct io_stats *io_curr, + struct io_stats *io_last, + struct io_stats *io_dead) { - struct io_stats *io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; + io_bucket->read_bytes += io_curr->read_bytes + io_dead->read_bytes - + io_last->read_bytes; + io_bucket->write_bytes += io_curr->write_bytes + io_dead->write_bytes - + io_last->write_bytes; + io_bucket->rchar += io_curr->rchar + io_dead->rchar - io_last->rchar; + io_bucket->wchar += io_curr->wchar + io_dead->wchar - io_last->wchar; + io_bucket->fsync += io_curr->fsync + io_dead->fsync - io_last->fsync; - io_last->read_bytes -= task->ioac.read_bytes; - io_last->write_bytes -= compute_write_bytes(task); - io_last->rchar -= task->ioac.rchar; - io_last->wchar -= task->ioac.wchar; - io_last->fsync -= task->ioac.syscfs; + io_last->read_bytes = io_curr->read_bytes; + io_last->write_bytes = io_curr->write_bytes; + io_last->rchar = io_curr->rchar; + io_last->wchar = io_curr->wchar; + io_last->fsync = io_curr->fsync; + + memset(io_dead, 0, sizeof(struct io_stats)); } static void update_io_stats_all_locked(void) { - struct uid_entry *uid_entry; + struct uid_entry *uid_entry = NULL; struct task_struct *task, *temp; - struct io_stats *io_bucket, *io_curr, *io_last; struct user_namespace *user_ns = current_user_ns(); unsigned long bkt; uid_t uid; @@ -254,73 +265,42 @@ static void update_io_stats_all_locked(void) rcu_read_lock(); do_each_thread(temp, task) { uid = from_kuid_munged(user_ns, task_uid(task)); - uid_entry = find_or_register_uid(uid); + if (!uid_entry || uid_entry->uid != uid) + uid_entry = find_or_register_uid(uid); if (!uid_entry) continue; - add_uid_io_curr_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR); } while_each_thread(temp, task); rcu_read_unlock(); hash_for_each(hash_table, bkt, uid_entry, hash) { - io_bucket = &uid_entry->io[uid_entry->state]; - io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; - io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; - - io_bucket->read_bytes += - io_curr->read_bytes - io_last->read_bytes; - io_bucket->write_bytes += - io_curr->write_bytes - io_last->write_bytes; - io_bucket->rchar += io_curr->rchar - io_last->rchar; - io_bucket->wchar += io_curr->wchar - io_last->wchar; - io_bucket->fsync += io_curr->fsync - io_last->fsync; - - io_last->read_bytes = io_curr->read_bytes; - io_last->write_bytes = io_curr->write_bytes; - io_last->rchar = io_curr->rchar; - io_last->wchar = io_curr->wchar; - io_last->fsync = io_curr->fsync; + compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + &uid_entry->io[UID_STATE_TOTAL_CURR], + &uid_entry->io[UID_STATE_TOTAL_LAST], + &uid_entry->io[UID_STATE_DEAD_TASKS]); } } -static void update_io_stats_uid_locked(uid_t target_uid) +static void update_io_stats_uid_locked(struct uid_entry *uid_entry) { - struct uid_entry *uid_entry; struct task_struct *task, *temp; - struct io_stats *io_bucket, *io_curr, *io_last; struct user_namespace *user_ns = current_user_ns(); - uid_entry = find_or_register_uid(target_uid); - if (!uid_entry) - return; - memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0, sizeof(struct io_stats)); rcu_read_lock(); do_each_thread(temp, task) { - if (from_kuid_munged(user_ns, task_uid(task)) != target_uid) + if (from_kuid_munged(user_ns, task_uid(task)) != uid_entry->uid) continue; - add_uid_io_curr_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_TOTAL_CURR); } while_each_thread(temp, task); rcu_read_unlock(); - io_bucket = &uid_entry->io[uid_entry->state]; - io_curr = &uid_entry->io[UID_STATE_TOTAL_CURR]; - io_last = &uid_entry->io[UID_STATE_TOTAL_LAST]; - - io_bucket->read_bytes += - io_curr->read_bytes - io_last->read_bytes; - io_bucket->write_bytes += - io_curr->write_bytes - io_last->write_bytes; - io_bucket->rchar += io_curr->rchar - io_last->rchar; - io_bucket->wchar += io_curr->wchar - io_last->wchar; - io_bucket->fsync += io_curr->fsync - io_last->fsync; - - io_last->read_bytes = io_curr->read_bytes; - io_last->write_bytes = io_curr->write_bytes; - io_last->rchar = io_curr->rchar; - io_last->wchar = io_curr->wchar; - io_last->fsync = io_curr->fsync; + compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state], + &uid_entry->io[UID_STATE_TOTAL_CURR], + &uid_entry->io[UID_STATE_TOTAL_LAST], + &uid_entry->io[UID_STATE_DEAD_TASKS]); } static int uid_io_show(struct seq_file *m, void *v) @@ -405,7 +385,7 @@ static ssize_t uid_procstat_write(struct file *file, return count; } - update_io_stats_uid_locked(uid); + update_io_stats_uid_locked(uid_entry); uid_entry->state = state; @@ -443,8 +423,7 @@ static int process_notifier(struct notifier_block *self, uid_entry->utime += utime; uid_entry->stime += stime; - update_io_stats_uid_locked(uid); - clean_uid_io_last_stats(uid_entry, task); + add_uid_io_stats(uid_entry, task, UID_STATE_DEAD_TASKS); exit: rt_mutex_unlock(&uid_lock); diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 69e51cc96303..29c57d13744e 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1017,7 +1017,7 @@ static int mmc_blk_ioctl_rpmb_cmd(struct block_device *bdev, { struct mmc_blk_ioc_rpmb_data *idata; struct mmc_blk_data *md; - struct mmc_card *card; + struct mmc_card *card = NULL; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct mmc_request mrq = {NULL}; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 63f7bf87843f..79b5b3504ccd 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -616,17 +616,39 @@ static int mmc_devfreq_create_freq_table(struct mmc_host *host) host->card->clk_scaling_lowest, host->card->clk_scaling_highest); + /* + * Create the frequency table and initialize it with default values. + * Initialize it with platform specific frequencies if the frequency + * table supplied by platform driver is present, otherwise initialize + * it with min and max frequencies supported by the card. + */ if (!clk_scaling->freq_table) { - pr_debug("%s: no frequency table defined - setting default\n", - mmc_hostname(host)); + if (clk_scaling->pltfm_freq_table_sz) + clk_scaling->freq_table_sz = + clk_scaling->pltfm_freq_table_sz; + else + clk_scaling->freq_table_sz = 2; + clk_scaling->freq_table = kzalloc( - 2*sizeof(*(clk_scaling->freq_table)), GFP_KERNEL); + (clk_scaling->freq_table_sz * + sizeof(*(clk_scaling->freq_table))), GFP_KERNEL); if (!clk_scaling->freq_table) return -ENOMEM; - clk_scaling->freq_table[0] = host->card->clk_scaling_lowest; - clk_scaling->freq_table[1] = host->card->clk_scaling_highest; - clk_scaling->freq_table_sz = 2; - goto out; + + if (clk_scaling->pltfm_freq_table) { + memcpy(clk_scaling->freq_table, + clk_scaling->pltfm_freq_table, + (clk_scaling->pltfm_freq_table_sz * + sizeof(*(clk_scaling->pltfm_freq_table)))); + } else { + pr_debug("%s: no frequency table defined - setting default\n", + mmc_hostname(host)); + clk_scaling->freq_table[0] = + host->card->clk_scaling_lowest; + clk_scaling->freq_table[1] = + host->card->clk_scaling_highest; + goto out; + } } if (host->card->clk_scaling_lowest > @@ -835,7 +857,7 @@ int mmc_resume_clk_scaling(struct mmc_host *host) devfreq_min_clk = host->clk_scaling.freq_table[0]; host->clk_scaling.curr_freq = devfreq_max_clk; - if (host->ios.clock < host->card->clk_scaling_highest) + if (host->ios.clock < host->clk_scaling.freq_table[max_clk_idx]) host->clk_scaling.curr_freq = devfreq_min_clk; host->clk_scaling.clk_scaling_in_progress = false; @@ -895,6 +917,10 @@ int mmc_exit_clk_scaling(struct mmc_host *host) host->clk_scaling.devfreq = NULL; atomic_set(&host->clk_scaling.devfreq_abort, 1); + + kfree(host->clk_scaling.freq_table); + host->clk_scaling.freq_table = NULL; + pr_debug("%s: devfreq was removed\n", mmc_hostname(host)); return 0; @@ -1709,7 +1735,6 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, struct mmc_async_req *areq, int *error) { int err = 0; - int start_err = 0; struct mmc_async_req *data = host->areq; /* Prepare a new request */ @@ -1758,7 +1783,7 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host, trace_mmc_blk_rw_start(areq->mrq->cmd->opcode, areq->mrq->cmd->arg, areq->mrq->data); - start_err = __mmc_start_data_req(host, areq->mrq); + __mmc_start_data_req(host, areq->mrq); } if (host->areq) diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 594fba08e623..72bfdd835178 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -337,10 +337,15 @@ static int mmc_force_err_set(void *data, u64 val) { struct mmc_host *host = data; - if (host && host->ops && host->ops->force_err_irq) { - mmc_host_clk_hold(host); + if (host && host->card && host->ops && + host->ops->force_err_irq) { + /* + * To access the force error irq reg, we need to make + * sure the host is powered up and host clock is ticking. + */ + mmc_get_card(host->card); host->ops->force_err_irq(host, val); - mmc_host_clk_release(host); + mmc_put_card(host->card); } return 0; diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 9ca73a2b86db..ae54302be8fd 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -327,6 +327,7 @@ void mmc_retune_enable(struct mmc_host *host) mod_timer(&host->retune_timer, jiffies + host->retune_period * HZ); } +EXPORT_SYMBOL(mmc_retune_enable); void mmc_retune_disable(struct mmc_host *host) { @@ -335,6 +336,7 @@ void mmc_retune_disable(struct mmc_host *host) host->retune_now = 0; host->need_retune = 0; } +EXPORT_SYMBOL(mmc_retune_disable); void mmc_retune_timer_stop(struct mmc_host *host) { diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 5b4d5d74fe55..5033107f6e26 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1283,6 +1283,11 @@ static int _mmc_sd_resume(struct mmc_host *host) #else err = mmc_sd_init_card(host, host->card->ocr, host->card); #endif + if (err) { + pr_err("%s: %s: mmc_sd_init_card_failed (%d)\n", + mmc_hostname(host), __func__, err); + goto out; + } mmc_card_clr_suspended(host->card); if (host->card->sdr104_blocked) diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c index 3b423b0ad8e7..f280744578e4 100644 --- a/drivers/mmc/host/sdhci-iproc.c +++ b/drivers/mmc/host/sdhci-iproc.c @@ -156,7 +156,8 @@ static const struct sdhci_ops sdhci_iproc_ops = { }; static const struct sdhci_pltfm_data sdhci_iproc_pltfm_data = { - .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK, + .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN, .ops = &sdhci_iproc_ops, }; diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index ca72ebfd55a3..d908c3fed7c9 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1823,7 +1823,7 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, } pdata->status_gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags); - if (gpio_is_valid(pdata->status_gpio) & !(flags & OF_GPIO_ACTIVE_LOW)) + if (gpio_is_valid(pdata->status_gpio) && !(flags & OF_GPIO_ACTIVE_LOW)) pdata->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; of_property_read_u32(np, "qcom,bus-width", &bus_width); @@ -1837,13 +1837,13 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, } if (sdhci_msm_dt_get_array(dev, "qcom,devfreq,freq-table", - &msm_host->mmc->clk_scaling.freq_table, - &msm_host->mmc->clk_scaling.freq_table_sz, 0)) + &msm_host->mmc->clk_scaling.pltfm_freq_table, + &msm_host->mmc->clk_scaling.pltfm_freq_table_sz, 0)) pr_debug("%s: no clock scaling frequencies were supplied\n", dev_name(dev)); - else if (!msm_host->mmc->clk_scaling.freq_table || - !msm_host->mmc->clk_scaling.freq_table_sz) - dev_err(dev, "bad dts clock scaling frequencies\n"); + else if (!msm_host->mmc->clk_scaling.pltfm_freq_table || + !msm_host->mmc->clk_scaling.pltfm_freq_table_sz) + dev_err(dev, "bad dts clock scaling frequencies\n"); /* * Few hosts can support DDR52 mode at the same lower @@ -1958,7 +1958,7 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, sdhci_msm_pm_qos_parse(dev, pdata); if (of_get_property(np, "qcom,core_3_0v_support", NULL)) - pdata->core_3_0v_support = true; + msm_host->core_3_0v_support = true; pdata->sdr104_wa = of_property_read_bool(np, "qcom,sdr104-wa"); @@ -2366,21 +2366,6 @@ out: return ret; } -/* - * Reset vreg by ensuring it is off during probe. A call - * to enable vreg is needed to balance disable vreg - */ -static int sdhci_msm_vreg_reset(struct sdhci_msm_pltfm_data *pdata) -{ - int ret; - - ret = sdhci_msm_setup_vreg(pdata, 1, true); - if (ret) - return ret; - ret = sdhci_msm_setup_vreg(pdata, 0, true); - return ret; -} - /* This init function should be called only once for each SDHC slot */ static int sdhci_msm_vreg_init(struct device *dev, struct sdhci_msm_pltfm_data *pdata, @@ -2415,7 +2400,7 @@ static int sdhci_msm_vreg_init(struct device *dev, if (ret) goto vdd_reg_deinit; } - ret = sdhci_msm_vreg_reset(pdata); + if (ret) dev_err(dev, "vreg reset failed (%d)\n", ret); goto out; @@ -2592,7 +2577,9 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) io_level = REQ_IO_HIGH; } if (irq_status & CORE_PWRCTL_BUS_OFF) { - ret = sdhci_msm_setup_vreg(msm_host->pdata, false, false); + if (msm_host->pltfm_init_done) + ret = sdhci_msm_setup_vreg(msm_host->pdata, + false, false); if (!ret) { ret = sdhci_msm_setup_pins(msm_host->pdata, false); ret |= sdhci_msm_set_vdd_io_vol(msm_host->pdata, @@ -2639,7 +2626,9 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) */ mb(); - if ((io_level & REQ_IO_HIGH) && (msm_host->caps_0 & CORE_3_0V_SUPPORT)) + if ((io_level & REQ_IO_HIGH) && + (msm_host->caps_0 & CORE_3_0V_SUPPORT) && + !msm_host->core_3_0v_support) writel_relaxed((readl_relaxed(host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC) & ~CORE_IO_PAD_PWR_SWITCH), host->ioaddr + @@ -4133,7 +4122,7 @@ static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host, msm_host->use_14lpp_dll = true; /* Fake 3.0V support for SDIO devices which requires such voltage */ - if (msm_host->pdata->core_3_0v_support) { + if (msm_host->core_3_0v_support) { caps |= CORE_3_0V_SUPPORT; writel_relaxed((readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES) | caps), host->ioaddr + @@ -4666,6 +4655,8 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto vreg_deinit; } + msm_host->pltfm_init_done = true; + pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, MSM_AUTOSUSPEND_DELAY_MS); diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h index 92f61708001e..79949c2c537f 100644 --- a/drivers/mmc/host/sdhci-msm.h +++ b/drivers/mmc/host/sdhci-msm.h @@ -152,7 +152,6 @@ struct sdhci_msm_pltfm_data { u32 ice_clk_max; u32 ice_clk_min; struct sdhci_msm_pm_qos_data pm_qos_data; - bool core_3_0v_support; bool sdr104_wa; }; @@ -226,6 +225,8 @@ struct sdhci_msm_host { bool tuning_in_progress; bool mci_removed; const struct sdhci_msm_offset *offset; + bool core_3_0v_support; + bool pltfm_init_done; }; extern char *saved_command_line; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 40a34c283955..ddb9947ce298 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2418,7 +2418,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) if (host->ops->platform_execute_tuning) { spin_unlock_irqrestore(&host->lock, flags); + /* + * Make sure re-tuning won't get triggered for the CRC errors + * occurred while executing tuning + */ + mmc_retune_disable(mmc); err = host->ops->platform_execute_tuning(host, opcode); + mmc_retune_enable(mmc); sdhci_runtime_pm_put(host); return err; } diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c index cbc99d5649af..ae5709354546 100644 --- a/drivers/net/can/usb/gs_usb.c +++ b/drivers/net/can/usb/gs_usb.c @@ -246,6 +246,8 @@ static int gs_cmd_reset(struct gs_usb *gsusb, struct gs_can *gsdev) sizeof(*dm), 1000); + kfree(dm); + return rc; } diff --git a/drivers/net/ethernet/adaptec/starfire.c b/drivers/net/ethernet/adaptec/starfire.c index ac7288240d55..f089fa954f42 100644 --- a/drivers/net/ethernet/adaptec/starfire.c +++ b/drivers/net/ethernet/adaptec/starfire.c @@ -1153,6 +1153,12 @@ static void init_ring(struct net_device *dev) if (skb == NULL) break; np->rx_info[i].mapping = pci_map_single(np->pci_dev, skb->data, np->rx_buf_sz, PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(np->pci_dev, + np->rx_info[i].mapping)) { + dev_kfree_skb(skb); + np->rx_info[i].skb = NULL; + break; + } /* Grrr, we cannot offset to correctly align the IP header. */ np->rx_ring[i].rxaddr = cpu_to_dma(np->rx_info[i].mapping | RxDescValid); } @@ -1183,8 +1189,9 @@ static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) { struct netdev_private *np = netdev_priv(dev); unsigned int entry; + unsigned int prev_tx; u32 status; - int i; + int i, j; /* * be cautious here, wrapping the queue has weird semantics @@ -1202,6 +1209,7 @@ static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) } #endif /* ZEROCOPY && HAS_BROKEN_FIRMWARE */ + prev_tx = np->cur_tx; entry = np->cur_tx % TX_RING_SIZE; for (i = 0; i < skb_num_frags(skb); i++) { int wrap_ring = 0; @@ -1235,6 +1243,11 @@ static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) skb_frag_size(this_frag), PCI_DMA_TODEVICE); } + if (pci_dma_mapping_error(np->pci_dev, + np->tx_info[entry].mapping)) { + dev->stats.tx_dropped++; + goto err_out; + } np->tx_ring[entry].addr = cpu_to_dma(np->tx_info[entry].mapping); np->tx_ring[entry].status = cpu_to_le32(status); @@ -1269,8 +1282,30 @@ static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); return NETDEV_TX_OK; -} +err_out: + entry = prev_tx % TX_RING_SIZE; + np->tx_info[entry].skb = NULL; + if (i > 0) { + pci_unmap_single(np->pci_dev, + np->tx_info[entry].mapping, + skb_first_frag_len(skb), + PCI_DMA_TODEVICE); + np->tx_info[entry].mapping = 0; + entry = (entry + np->tx_info[entry].used_slots) % TX_RING_SIZE; + for (j = 1; j < i; j++) { + pci_unmap_single(np->pci_dev, + np->tx_info[entry].mapping, + skb_frag_size( + &skb_shinfo(skb)->frags[j-1]), + PCI_DMA_TODEVICE); + entry++; + } + } + dev_kfree_skb_any(skb); + np->cur_tx = prev_tx; + return NETDEV_TX_OK; +} /* The interrupt handler does all of the Rx thread work and cleans up after the Tx thread. */ @@ -1570,6 +1605,12 @@ static void refill_rx_ring(struct net_device *dev) break; /* Better luck next round. */ np->rx_info[entry].mapping = pci_map_single(np->pci_dev, skb->data, np->rx_buf_sz, PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(np->pci_dev, + np->rx_info[entry].mapping)) { + dev_kfree_skb(skb); + np->rx_info[entry].skb = NULL; + break; + } np->rx_ring[entry].rxaddr = cpu_to_dma(np->rx_info[entry].mapping | RxDescValid); } diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index c82ab87fcbe8..e5911ccb2148 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -1949,7 +1949,7 @@ u16 bnx2x_select_queue(struct net_device *dev, struct sk_buff *skb, } /* select a non-FCoE queue */ - return fallback(dev, skb) % BNX2X_NUM_ETH_QUEUES(bp); + return fallback(dev, skb) % (BNX2X_NUM_ETH_QUEUES(bp) * bp->max_cos); } void bnx2x_set_num_queues(struct bnx2x *bp) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 0d147610a06f..090e00650601 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2714,10 +2714,14 @@ static int cxgb_up(struct adapter *adap) if (err) goto irq_err; } + + mutex_lock(&uld_mutex); enable_rx(adap); t4_sge_start(adap); t4_intr_enable(adap); adap->flags |= FULL_INIT_DONE; + mutex_unlock(&uld_mutex); + notify_ulds(adap, CXGB4_STATE_UP); #if IS_ENABLED(CONFIG_IPV6) update_clip(adap); diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 8a1d9fffd7d6..26255862d1cf 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -5260,9 +5260,11 @@ static netdev_features_t be_features_check(struct sk_buff *skb, struct be_adapter *adapter = netdev_priv(dev); u8 l4_hdr = 0; - /* The code below restricts offload features for some tunneled packets. + /* The code below restricts offload features for some tunneled and + * Q-in-Q packets. * Offload features for normal (non tunnel) packets are unchanged. */ + features = vlan_features_check(skb, features); if (!skb->encapsulation || !(adapter->flags & BE_FLAGS_VXLAN_OFFLOADS)) return features; diff --git a/drivers/net/ethernet/ethoc.c b/drivers/net/ethernet/ethoc.c index ff665493ca97..52f2230062e7 100644 --- a/drivers/net/ethernet/ethoc.c +++ b/drivers/net/ethernet/ethoc.c @@ -713,6 +713,8 @@ static int ethoc_open(struct net_device *dev) if (ret) return ret; + napi_enable(&priv->napi); + ethoc_init_ring(priv, dev->mem_start); ethoc_reset(priv); @@ -725,7 +727,6 @@ static int ethoc_open(struct net_device *dev) } phy_start(priv->phy); - napi_enable(&priv->napi); if (netif_msg_ifup(priv)) { dev_info(&dev->dev, "I/O: %08lx Memory: %08lx-%08lx\n", diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 3e233d924cce..6a061f17a44f 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -1999,8 +1999,8 @@ static void free_skb_rx_queue(struct gfar_priv_rx_q *rx_queue) if (!rxb->page) continue; - dma_unmap_single(rx_queue->dev, rxb->dma, - PAGE_SIZE, DMA_FROM_DEVICE); + dma_unmap_page(rx_queue->dev, rxb->dma, + PAGE_SIZE, DMA_FROM_DEVICE); __free_page(rxb->page); rxb->page = NULL; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 08cef0dfb5db..2fa54b0b0679 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -105,8 +105,8 @@ int hns_nic_net_xmit_hw(struct net_device *ndev, struct hns_nic_ring_data *ring_data) { struct hns_nic_priv *priv = netdev_priv(ndev); - struct device *dev = priv->dev; struct hnae_ring *ring = ring_data->ring; + struct device *dev = ring_to_dev(ring); struct netdev_queue *dev_queue; struct skb_frag_struct *frag; int buf_num; diff --git a/drivers/net/ethernet/mellanox/mlx4/catas.c b/drivers/net/ethernet/mellanox/mlx4/catas.c index 715de8affcc9..e203d0c4e5a3 100644 --- a/drivers/net/ethernet/mellanox/mlx4/catas.c +++ b/drivers/net/ethernet/mellanox/mlx4/catas.c @@ -158,7 +158,7 @@ static int mlx4_reset_slave(struct mlx4_dev *dev) return -ETIMEDOUT; } -static int mlx4_comm_internal_err(u32 slave_read) +int mlx4_comm_internal_err(u32 slave_read) { return (u32)COMM_CHAN_EVENT_INTERNAL_ERR == (slave_read & (u32)COMM_CHAN_EVENT_INTERNAL_ERR) ? 1 : 0; diff --git a/drivers/net/ethernet/mellanox/mlx4/intf.c b/drivers/net/ethernet/mellanox/mlx4/intf.c index 0472941af820..1a134e08f010 100644 --- a/drivers/net/ethernet/mellanox/mlx4/intf.c +++ b/drivers/net/ethernet/mellanox/mlx4/intf.c @@ -218,6 +218,18 @@ void mlx4_unregister_device(struct mlx4_dev *dev) struct mlx4_interface *intf; mlx4_stop_catas_poll(dev); + if (dev->persist->interface_state & MLX4_INTERFACE_STATE_DELETION && + mlx4_is_slave(dev)) { + /* In mlx4_remove_one on a VF */ + u32 slave_read = + swab32(readl(&mlx4_priv(dev)->mfunc.comm->slave_read)); + + if (mlx4_comm_internal_err(slave_read)) { + mlx4_dbg(dev, "%s: comm channel is down, entering error state.\n", + __func__); + mlx4_enter_error_state(dev->persist); + } + } mutex_lock(&intf_mutex); list_for_each_entry(intf, &intf_list, list) diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index e1cf9036af22..f5fdbd53d052 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -1205,6 +1205,7 @@ void mlx4_qp_event(struct mlx4_dev *dev, u32 qpn, int event_type); void mlx4_srq_event(struct mlx4_dev *dev, u32 srqn, int event_type); void mlx4_enter_error_state(struct mlx4_dev_persistent *persist); +int mlx4_comm_internal_err(u32 slave_read); int mlx4_SENSE_PORT(struct mlx4_dev *dev, int port, enum mlx4_port_type *type); diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 549ad2018e7f..1e61d4da72db 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -171,6 +171,49 @@ static struct mdiobb_ops bb_ops = { .get_mdio_data = ravb_get_mdio_data, }; +/* Free TX skb function for AVB-IP */ +static int ravb_tx_free(struct net_device *ndev, int q, bool free_txed_only) +{ + struct ravb_private *priv = netdev_priv(ndev); + struct net_device_stats *stats = &priv->stats[q]; + struct ravb_tx_desc *desc; + int free_num = 0; + int entry; + u32 size; + + for (; priv->cur_tx[q] - priv->dirty_tx[q] > 0; priv->dirty_tx[q]++) { + bool txed; + + entry = priv->dirty_tx[q] % (priv->num_tx_ring[q] * + NUM_TX_DESC); + desc = &priv->tx_ring[q][entry]; + txed = desc->die_dt == DT_FEMPTY; + if (free_txed_only && !txed) + break; + /* Descriptor type must be checked before all other reads */ + dma_rmb(); + size = le16_to_cpu(desc->ds_tagl) & TX_DS; + /* Free the original skb. */ + if (priv->tx_skb[q][entry / NUM_TX_DESC]) { + dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr), + size, DMA_TO_DEVICE); + /* Last packet descriptor? */ + if (entry % NUM_TX_DESC == NUM_TX_DESC - 1) { + entry /= NUM_TX_DESC; + dev_kfree_skb_any(priv->tx_skb[q][entry]); + priv->tx_skb[q][entry] = NULL; + if (txed) + stats->tx_packets++; + } + free_num++; + } + if (txed) + stats->tx_bytes += size; + desc->die_dt = DT_EEMPTY; + } + return free_num; +} + /* Free skb's and DMA buffers for Ethernet AVB */ static void ravb_ring_free(struct net_device *ndev, int q) { @@ -186,19 +229,21 @@ static void ravb_ring_free(struct net_device *ndev, int q) kfree(priv->rx_skb[q]); priv->rx_skb[q] = NULL; - /* Free TX skb ringbuffer */ - if (priv->tx_skb[q]) { - for (i = 0; i < priv->num_tx_ring[q]; i++) - dev_kfree_skb(priv->tx_skb[q][i]); - } - kfree(priv->tx_skb[q]); - priv->tx_skb[q] = NULL; - /* Free aligned TX buffers */ kfree(priv->tx_align[q]); priv->tx_align[q] = NULL; if (priv->rx_ring[q]) { + for (i = 0; i < priv->num_rx_ring[q]; i++) { + struct ravb_ex_rx_desc *desc = &priv->rx_ring[q][i]; + + if (!dma_mapping_error(ndev->dev.parent, + le32_to_cpu(desc->dptr))) + dma_unmap_single(ndev->dev.parent, + le32_to_cpu(desc->dptr), + PKT_BUF_SZ, + DMA_FROM_DEVICE); + } ring_size = sizeof(struct ravb_ex_rx_desc) * (priv->num_rx_ring[q] + 1); dma_free_coherent(ndev->dev.parent, ring_size, priv->rx_ring[q], @@ -207,12 +252,20 @@ static void ravb_ring_free(struct net_device *ndev, int q) } if (priv->tx_ring[q]) { + ravb_tx_free(ndev, q, false); + ring_size = sizeof(struct ravb_tx_desc) * (priv->num_tx_ring[q] * NUM_TX_DESC + 1); dma_free_coherent(ndev->dev.parent, ring_size, priv->tx_ring[q], priv->tx_desc_dma[q]); priv->tx_ring[q] = NULL; } + + /* Free TX skb ringbuffer. + * SKBs are freed by ravb_tx_free() call above. + */ + kfree(priv->tx_skb[q]); + priv->tx_skb[q] = NULL; } /* Format skb and descriptor buffer for Ethernet AVB */ @@ -420,44 +473,6 @@ static int ravb_dmac_init(struct net_device *ndev) return 0; } -/* Free TX skb function for AVB-IP */ -static int ravb_tx_free(struct net_device *ndev, int q) -{ - struct ravb_private *priv = netdev_priv(ndev); - struct net_device_stats *stats = &priv->stats[q]; - struct ravb_tx_desc *desc; - int free_num = 0; - int entry; - u32 size; - - for (; priv->cur_tx[q] - priv->dirty_tx[q] > 0; priv->dirty_tx[q]++) { - entry = priv->dirty_tx[q] % (priv->num_tx_ring[q] * - NUM_TX_DESC); - desc = &priv->tx_ring[q][entry]; - if (desc->die_dt != DT_FEMPTY) - break; - /* Descriptor type must be checked before all other reads */ - dma_rmb(); - size = le16_to_cpu(desc->ds_tagl) & TX_DS; - /* Free the original skb. */ - if (priv->tx_skb[q][entry / NUM_TX_DESC]) { - dma_unmap_single(ndev->dev.parent, le32_to_cpu(desc->dptr), - size, DMA_TO_DEVICE); - /* Last packet descriptor? */ - if (entry % NUM_TX_DESC == NUM_TX_DESC - 1) { - entry /= NUM_TX_DESC; - dev_kfree_skb_any(priv->tx_skb[q][entry]); - priv->tx_skb[q][entry] = NULL; - stats->tx_packets++; - } - free_num++; - } - stats->tx_bytes += size; - desc->die_dt = DT_EEMPTY; - } - return free_num; -} - static void ravb_get_tx_tstamp(struct net_device *ndev) { struct ravb_private *priv = netdev_priv(ndev); @@ -797,7 +812,7 @@ static int ravb_poll(struct napi_struct *napi, int budget) spin_lock_irqsave(&priv->lock, flags); /* Clear TX interrupt */ ravb_write(ndev, ~mask, TIS); - ravb_tx_free(ndev, q); + ravb_tx_free(ndev, q, true); netif_wake_subqueue(ndev, q); mmiowb(); spin_unlock_irqrestore(&priv->lock, flags); @@ -1393,7 +1408,8 @@ static netdev_tx_t ravb_start_xmit(struct sk_buff *skb, struct net_device *ndev) priv->cur_tx[q] += NUM_TX_DESC; if (priv->cur_tx[q] - priv->dirty_tx[q] > - (priv->num_tx_ring[q] - 1) * NUM_TX_DESC && !ravb_tx_free(ndev, q)) + (priv->num_tx_ring[q] - 1) * NUM_TX_DESC && + !ravb_tx_free(ndev, q, true)) netif_stop_subqueue(ndev, q); exit: diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index cf468c87ce57..4cb8b85cbf2c 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -100,6 +100,14 @@ /* BUFFER_ALIGN(adr) calculates the number of bytes to the next alignment. */ #define BUFFER_ALIGN(adr) ((ALIGNMENT - ((u32) adr)) % ALIGNMENT) +#ifdef __BIG_ENDIAN +#define xemaclite_readl ioread32be +#define xemaclite_writel iowrite32be +#else +#define xemaclite_readl ioread32 +#define xemaclite_writel iowrite32 +#endif + /** * struct net_local - Our private per device data * @ndev: instance of the network device @@ -158,15 +166,15 @@ static void xemaclite_enable_interrupts(struct net_local *drvdata) u32 reg_data; /* Enable the Tx interrupts for the first Buffer */ - reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET); - __raw_writel(reg_data | XEL_TSR_XMIT_IE_MASK, - drvdata->base_addr + XEL_TSR_OFFSET); + reg_data = xemaclite_readl(drvdata->base_addr + XEL_TSR_OFFSET); + xemaclite_writel(reg_data | XEL_TSR_XMIT_IE_MASK, + drvdata->base_addr + XEL_TSR_OFFSET); /* Enable the Rx interrupts for the first buffer */ - __raw_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET); + xemaclite_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET); /* Enable the Global Interrupt Enable */ - __raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET); + xemaclite_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET); } /** @@ -181,17 +189,17 @@ static void xemaclite_disable_interrupts(struct net_local *drvdata) u32 reg_data; /* Disable the Global Interrupt Enable */ - __raw_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET); + xemaclite_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET); /* Disable the Tx interrupts for the first buffer */ - reg_data = __raw_readl(drvdata->base_addr + XEL_TSR_OFFSET); - __raw_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK), - drvdata->base_addr + XEL_TSR_OFFSET); + reg_data = xemaclite_readl(drvdata->base_addr + XEL_TSR_OFFSET); + xemaclite_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK), + drvdata->base_addr + XEL_TSR_OFFSET); /* Disable the Rx interrupts for the first buffer */ - reg_data = __raw_readl(drvdata->base_addr + XEL_RSR_OFFSET); - __raw_writel(reg_data & (~XEL_RSR_RECV_IE_MASK), - drvdata->base_addr + XEL_RSR_OFFSET); + reg_data = xemaclite_readl(drvdata->base_addr + XEL_RSR_OFFSET); + xemaclite_writel(reg_data & (~XEL_RSR_RECV_IE_MASK), + drvdata->base_addr + XEL_RSR_OFFSET); } /** @@ -323,7 +331,7 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data, byte_count = ETH_FRAME_LEN; /* Check if the expected buffer is available */ - reg_data = __raw_readl(addr + XEL_TSR_OFFSET); + reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET); if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK)) == 0) { @@ -336,7 +344,7 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data, addr = (void __iomem __force *)((u32 __force)addr ^ XEL_BUFFER_OFFSET); - reg_data = __raw_readl(addr + XEL_TSR_OFFSET); + reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET); if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK)) != 0) @@ -347,16 +355,16 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data, /* Write the frame to the buffer */ xemaclite_aligned_write(data, (u32 __force *) addr, byte_count); - __raw_writel((byte_count & XEL_TPLR_LENGTH_MASK), - addr + XEL_TPLR_OFFSET); + xemaclite_writel((byte_count & XEL_TPLR_LENGTH_MASK), + addr + XEL_TPLR_OFFSET); /* Update the Tx Status Register to indicate that there is a * frame to send. Set the XEL_TSR_XMIT_ACTIVE_MASK flag which * is used by the interrupt handler to check whether a frame * has been transmitted */ - reg_data = __raw_readl(addr + XEL_TSR_OFFSET); + reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET); reg_data |= (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK); - __raw_writel(reg_data, addr + XEL_TSR_OFFSET); + xemaclite_writel(reg_data, addr + XEL_TSR_OFFSET); return 0; } @@ -371,7 +379,7 @@ static int xemaclite_send_data(struct net_local *drvdata, u8 *data, * * Return: Total number of bytes received */ -static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) +static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data, int maxlen) { void __iomem *addr; u16 length, proto_type; @@ -381,7 +389,7 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use); /* Verify which buffer has valid data */ - reg_data = __raw_readl(addr + XEL_RSR_OFFSET); + reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET); if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) { if (drvdata->rx_ping_pong != 0) @@ -398,27 +406,28 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) return 0; /* No data was available */ /* Verify that buffer has valid data */ - reg_data = __raw_readl(addr + XEL_RSR_OFFSET); + reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET); if ((reg_data & XEL_RSR_RECV_DONE_MASK) != XEL_RSR_RECV_DONE_MASK) return 0; /* No data was available */ } /* Get the protocol type of the ethernet frame that arrived */ - proto_type = ((ntohl(__raw_readl(addr + XEL_HEADER_OFFSET + + proto_type = ((ntohl(xemaclite_readl(addr + XEL_HEADER_OFFSET + XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) & XEL_RPLR_LENGTH_MASK); /* Check if received ethernet frame is a raw ethernet frame * or an IP packet or an ARP packet */ - if (proto_type > (ETH_FRAME_LEN + ETH_FCS_LEN)) { + if (proto_type > ETH_DATA_LEN) { if (proto_type == ETH_P_IP) { - length = ((ntohl(__raw_readl(addr + + length = ((ntohl(xemaclite_readl(addr + XEL_HEADER_IP_LENGTH_OFFSET + XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) & XEL_RPLR_LENGTH_MASK); + length = min_t(u16, length, ETH_DATA_LEN); length += ETH_HLEN + ETH_FCS_LEN; } else if (proto_type == ETH_P_ARP) @@ -431,14 +440,17 @@ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data) /* Use the length in the frame, plus the header and trailer */ length = proto_type + ETH_HLEN + ETH_FCS_LEN; + if (WARN_ON(length > maxlen)) + length = maxlen; + /* Read from the EmacLite device */ xemaclite_aligned_read((u32 __force *) (addr + XEL_RXBUFF_OFFSET), data, length); /* Acknowledge the frame */ - reg_data = __raw_readl(addr + XEL_RSR_OFFSET); + reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET); reg_data &= ~XEL_RSR_RECV_DONE_MASK; - __raw_writel(reg_data, addr + XEL_RSR_OFFSET); + xemaclite_writel(reg_data, addr + XEL_RSR_OFFSET); return length; } @@ -465,14 +477,14 @@ static void xemaclite_update_address(struct net_local *drvdata, xemaclite_aligned_write(address_ptr, (u32 __force *) addr, ETH_ALEN); - __raw_writel(ETH_ALEN, addr + XEL_TPLR_OFFSET); + xemaclite_writel(ETH_ALEN, addr + XEL_TPLR_OFFSET); /* Update the MAC address in the EmacLite */ - reg_data = __raw_readl(addr + XEL_TSR_OFFSET); - __raw_writel(reg_data | XEL_TSR_PROG_MAC_ADDR, addr + XEL_TSR_OFFSET); + reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET); + xemaclite_writel(reg_data | XEL_TSR_PROG_MAC_ADDR, addr + XEL_TSR_OFFSET); /* Wait for EmacLite to finish with the MAC address update */ - while ((__raw_readl(addr + XEL_TSR_OFFSET) & + while ((xemaclite_readl(addr + XEL_TSR_OFFSET) & XEL_TSR_PROG_MAC_ADDR) != 0) ; } @@ -605,7 +617,7 @@ static void xemaclite_rx_handler(struct net_device *dev) skb_reserve(skb, 2); - len = xemaclite_recv_data(lp, (u8 *) skb->data); + len = xemaclite_recv_data(lp, (u8 *) skb->data, len); if (!len) { dev->stats.rx_errors++; @@ -642,32 +654,32 @@ static irqreturn_t xemaclite_interrupt(int irq, void *dev_id) u32 tx_status; /* Check if there is Rx Data available */ - if ((__raw_readl(base_addr + XEL_RSR_OFFSET) & + if ((xemaclite_readl(base_addr + XEL_RSR_OFFSET) & XEL_RSR_RECV_DONE_MASK) || - (__raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET) + (xemaclite_readl(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET) & XEL_RSR_RECV_DONE_MASK)) xemaclite_rx_handler(dev); /* Check if the Transmission for the first buffer is completed */ - tx_status = __raw_readl(base_addr + XEL_TSR_OFFSET); + tx_status = xemaclite_readl(base_addr + XEL_TSR_OFFSET); if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK; - __raw_writel(tx_status, base_addr + XEL_TSR_OFFSET); + xemaclite_writel(tx_status, base_addr + XEL_TSR_OFFSET); tx_complete = true; } /* Check if the Transmission for the second buffer is completed */ - tx_status = __raw_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); + tx_status = xemaclite_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK; - __raw_writel(tx_status, base_addr + XEL_BUFFER_OFFSET + - XEL_TSR_OFFSET); + xemaclite_writel(tx_status, base_addr + XEL_BUFFER_OFFSET + + XEL_TSR_OFFSET); tx_complete = true; } @@ -700,7 +712,7 @@ static int xemaclite_mdio_wait(struct net_local *lp) /* wait for the MDIO interface to not be busy or timeout after some time. */ - while (__raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) & + while (xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET) & XEL_MDIOCTRL_MDIOSTS_MASK) { if (time_before_eq(end, jiffies)) { WARN_ON(1); @@ -736,17 +748,17 @@ static int xemaclite_mdio_read(struct mii_bus *bus, int phy_id, int reg) * MDIO Address register. Set the Status bit in the MDIO Control * register to start a MDIO read transaction. */ - ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET); - __raw_writel(XEL_MDIOADDR_OP_MASK | - ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg), - lp->base_addr + XEL_MDIOADDR_OFFSET); - __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, - lp->base_addr + XEL_MDIOCTRL_OFFSET); + ctrl_reg = xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET); + xemaclite_writel(XEL_MDIOADDR_OP_MASK | + ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg), + lp->base_addr + XEL_MDIOADDR_OFFSET); + xemaclite_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, + lp->base_addr + XEL_MDIOCTRL_OFFSET); if (xemaclite_mdio_wait(lp)) return -ETIMEDOUT; - rc = __raw_readl(lp->base_addr + XEL_MDIORD_OFFSET); + rc = xemaclite_readl(lp->base_addr + XEL_MDIORD_OFFSET); dev_dbg(&lp->ndev->dev, "xemaclite_mdio_read(phy_id=%i, reg=%x) == %x\n", @@ -783,13 +795,13 @@ static int xemaclite_mdio_write(struct mii_bus *bus, int phy_id, int reg, * Data register. Finally, set the Status bit in the MDIO Control * register to start a MDIO write transaction. */ - ctrl_reg = __raw_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET); - __raw_writel(~XEL_MDIOADDR_OP_MASK & - ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg), - lp->base_addr + XEL_MDIOADDR_OFFSET); - __raw_writel(val, lp->base_addr + XEL_MDIOWR_OFFSET); - __raw_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, - lp->base_addr + XEL_MDIOCTRL_OFFSET); + ctrl_reg = xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET); + xemaclite_writel(~XEL_MDIOADDR_OP_MASK & + ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg), + lp->base_addr + XEL_MDIOADDR_OFFSET); + xemaclite_writel(val, lp->base_addr + XEL_MDIOWR_OFFSET); + xemaclite_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, + lp->base_addr + XEL_MDIOCTRL_OFFSET); return 0; } @@ -836,8 +848,8 @@ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) /* Enable the MDIO bus by asserting the enable bit in MDIO Control * register. */ - __raw_writel(XEL_MDIOCTRL_MDIOEN_MASK, - lp->base_addr + XEL_MDIOCTRL_OFFSET); + xemaclite_writel(XEL_MDIOCTRL_MDIOEN_MASK, + lp->base_addr + XEL_MDIOCTRL_OFFSET); bus = mdiobus_alloc(); if (!bus) { @@ -1141,8 +1153,8 @@ static int xemaclite_of_probe(struct platform_device *ofdev) dev_warn(dev, "No MAC address found\n"); /* Clear the Tx CSR's in case this is a restart */ - __raw_writel(0, lp->base_addr + XEL_TSR_OFFSET); - __raw_writel(0, lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); + xemaclite_writel(0, lp->base_addr + XEL_TSR_OFFSET); + xemaclite_writel(0, lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); /* Set the MAC address in the EmacLite device */ xemaclite_update_address(lp, ndev->dev_addr); diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 85828f153445..0758d0816840 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -648,8 +648,8 @@ static void ax_setup(struct net_device *dev) { /* Finish setting up the DEVICE info. */ dev->mtu = AX_MTU; - dev->hard_header_len = 0; - dev->addr_len = 0; + dev->hard_header_len = AX25_MAX_HEADER_LEN; + dev->addr_len = AX25_ADDR_LEN; dev->type = ARPHRD_AX25; dev->tx_queue_len = 10; dev->header_ops = &ax25_header_ops; diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 25f21968fa5c..de2ea9f2f966 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -1077,7 +1077,7 @@ static int stir421x_patch_device(struct irda_usb_cb *self) * are "42101001.sb" or "42101002.sb" */ sprintf(stir421x_fw_name, "4210%4X.sb", - self->usbdev->descriptor.bcdDevice); + le16_to_cpu(self->usbdev->descriptor.bcdDevice)); ret = request_firmware(&fw, stir421x_fw_name, &self->usbdev->dev); if (ret < 0) return ret; diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 0240552b50f3..d2701c53ed68 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -203,34 +203,6 @@ static int marvell_config_aneg(struct phy_device *phydev) { int err; - /* The Marvell PHY has an errata which requires - * that certain registers get written in order - * to restart autonegotiation */ - err = phy_write(phydev, MII_BMCR, BMCR_RESET); - - if (err < 0) - return err; - - err = phy_write(phydev, 0x1d, 0x1f); - if (err < 0) - return err; - - err = phy_write(phydev, 0x1e, 0x200c); - if (err < 0) - return err; - - err = phy_write(phydev, 0x1d, 0x5); - if (err < 0) - return err; - - err = phy_write(phydev, 0x1e, 0); - if (err < 0) - return err; - - err = phy_write(phydev, 0x1e, 0x100); - if (err < 0) - return err; - err = marvell_set_polarity(phydev, phydev->mdix); if (err < 0) return err; @@ -264,6 +236,42 @@ static int marvell_config_aneg(struct phy_device *phydev) return 0; } +static int m88e1101_config_aneg(struct phy_device *phydev) +{ + int err; + + /* This Marvell PHY has an errata which requires + * that certain registers get written in order + * to restart autonegotiation + */ + err = phy_write(phydev, MII_BMCR, BMCR_RESET); + + if (err < 0) + return err; + + err = phy_write(phydev, 0x1d, 0x1f); + if (err < 0) + return err; + + err = phy_write(phydev, 0x1e, 0x200c); + if (err < 0) + return err; + + err = phy_write(phydev, 0x1d, 0x5); + if (err < 0) + return err; + + err = phy_write(phydev, 0x1e, 0); + if (err < 0) + return err; + + err = phy_write(phydev, 0x1e, 0x100); + if (err < 0) + return err; + + return marvell_config_aneg(phydev); +} + #ifdef CONFIG_OF_MDIO /* * Set and/or override some configuration registers based on the @@ -993,7 +1001,7 @@ static struct phy_driver marvell_drivers[] = { .name = "Marvell 88E1101", .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, - .config_aneg = &marvell_config_aneg, + .config_aneg = &m88e1101_config_aneg, .read_status = &genphy_read_status, .ack_interrupt = &marvell_ack_interrupt, .config_intr = &marvell_config_intr, diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 09052f9e324f..582d8f0c6266 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -730,6 +730,8 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1199, 0x9071, 10)}, /* Sierra Wireless MC74xx */ {QMI_FIXED_INTF(0x1199, 0x9079, 8)}, /* Sierra Wireless EM74xx */ {QMI_FIXED_INTF(0x1199, 0x9079, 10)}, /* Sierra Wireless EM74xx */ + {QMI_FIXED_INTF(0x1199, 0x907b, 8)}, /* Sierra Wireless EM74xx */ + {QMI_FIXED_INTF(0x1199, 0x907b, 10)}, /* Sierra Wireless EM74xx */ {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */ {QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */ {QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */ @@ -754,6 +756,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x413c, 0x81b1, 8)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */ {QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */ + {QMI_FIXED_INTF(0x1e0e, 0x9001, 5)}, /* SIMCom 7230E */ /* 4. Gobi 1000 devices */ {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index fbb1867ff25c..1c27e6fb99f9 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -1851,6 +1851,9 @@ static int r8152_poll(struct napi_struct *napi, int budget) napi_complete(napi); if (!list_empty(&tp->rx_done)) napi_schedule(napi); + else if (!skb_queue_empty(&tp->tx_queue) && + !list_empty(&tp->tx_free)) + napi_schedule(napi); } return work_done; @@ -2990,10 +2993,13 @@ static void set_carrier(struct r8152 *tp) if (!netif_carrier_ok(netdev)) { tp->rtl_ops.enable(tp); set_bit(RTL8152_SET_RX_MODE, &tp->flags); + netif_stop_queue(netdev); napi_disable(&tp->napi); netif_carrier_on(netdev); rtl_start_rx(tp); napi_enable(&tp->napi); + netif_wake_queue(netdev); + netif_info(tp, link, netdev, "carrier on\n"); } } else { if (netif_carrier_ok(netdev)) { @@ -3001,6 +3007,7 @@ static void set_carrier(struct r8152 *tp) napi_disable(&tp->napi); tp->rtl_ops.disable(tp); napi_enable(&tp->napi); + netif_info(tp, link, netdev, "carrier off\n"); } } } @@ -3385,12 +3392,12 @@ static int rtl8152_pre_reset(struct usb_interface *intf) if (!netif_running(netdev)) return 0; + netif_stop_queue(netdev); napi_disable(&tp->napi); clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); cancel_delayed_work_sync(&tp->schedule); if (netif_carrier_ok(netdev)) { - netif_stop_queue(netdev); mutex_lock(&tp->control); tp->rtl_ops.disable(tp); mutex_unlock(&tp->control); @@ -3415,12 +3422,14 @@ static int rtl8152_post_reset(struct usb_interface *intf) if (netif_carrier_ok(netdev)) { mutex_lock(&tp->control); tp->rtl_ops.enable(tp); + rtl_start_rx(tp); rtl8152_set_rx_mode(netdev); mutex_unlock(&tp->control); - netif_wake_queue(netdev); } napi_enable(&tp->napi); + netif_wake_queue(netdev); + usb_submit_urb(tp->intr_urb, GFP_KERNEL); return 0; } diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c index a251588762ec..0b5a84c9022c 100644 --- a/drivers/net/usb/sierra_net.c +++ b/drivers/net/usb/sierra_net.c @@ -73,8 +73,6 @@ static atomic_t iface_counter = ATOMIC_INIT(0); /* Private data structure */ struct sierra_net_data { - u8 ethr_hdr_tmpl[ETH_HLEN]; /* ethernet header template for rx'd pkts */ - u16 link_up; /* air link up or down */ u8 tx_hdr_template[4]; /* part of HIP hdr for tx'd packets */ @@ -122,6 +120,7 @@ struct param { /* LSI Protocol types */ #define SIERRA_NET_PROTOCOL_UMTS 0x01 +#define SIERRA_NET_PROTOCOL_UMTS_DS 0x04 /* LSI Coverage */ #define SIERRA_NET_COVERAGE_NONE 0x00 #define SIERRA_NET_COVERAGE_NOPACKET 0x01 @@ -129,7 +128,8 @@ struct param { /* LSI Session */ #define SIERRA_NET_SESSION_IDLE 0x00 /* LSI Link types */ -#define SIERRA_NET_AS_LINK_TYPE_IPv4 0x00 +#define SIERRA_NET_AS_LINK_TYPE_IPV4 0x00 +#define SIERRA_NET_AS_LINK_TYPE_IPV6 0x02 struct lsi_umts { u8 protocol; @@ -137,9 +137,14 @@ struct lsi_umts { __be16 length; /* eventually use a union for the rest - assume umts for now */ u8 coverage; - u8 unused2[41]; + u8 network_len; /* network name len */ + u8 network[40]; /* network name (UCS2, bigendian) */ u8 session_state; u8 unused3[33]; +} __packed; + +struct lsi_umts_single { + struct lsi_umts lsi; u8 link_type; u8 pdp_addr_len; /* NW-supplied PDP address len */ u8 pdp_addr[16]; /* NW-supplied PDP address (bigendian)) */ @@ -158,10 +163,31 @@ struct lsi_umts { u8 reserved[8]; } __packed; +struct lsi_umts_dual { + struct lsi_umts lsi; + u8 pdp_addr4_len; /* NW-supplied PDP IPv4 address len */ + u8 pdp_addr4[4]; /* NW-supplied PDP IPv4 address (bigendian)) */ + u8 pdp_addr6_len; /* NW-supplied PDP IPv6 address len */ + u8 pdp_addr6[16]; /* NW-supplied PDP IPv6 address (bigendian)) */ + u8 unused4[23]; + u8 dns1_addr4_len; /* NW-supplied 1st DNS v4 address len (bigendian) */ + u8 dns1_addr4[4]; /* NW-supplied 1st DNS v4 address */ + u8 dns1_addr6_len; /* NW-supplied 1st DNS v6 address len */ + u8 dns1_addr6[16]; /* NW-supplied 1st DNS v6 address (bigendian)*/ + u8 dns2_addr4_len; /* NW-supplied 2nd DNS v4 address len (bigendian) */ + u8 dns2_addr4[4]; /* NW-supplied 2nd DNS v4 address */ + u8 dns2_addr6_len; /* NW-supplied 2nd DNS v6 address len */ + u8 dns2_addr6[16]; /* NW-supplied 2nd DNS v6 address (bigendian)*/ + u8 unused5[68]; +} __packed; + #define SIERRA_NET_LSI_COMMON_LEN 4 -#define SIERRA_NET_LSI_UMTS_LEN (sizeof(struct lsi_umts)) +#define SIERRA_NET_LSI_UMTS_LEN (sizeof(struct lsi_umts_single)) #define SIERRA_NET_LSI_UMTS_STATUS_LEN \ (SIERRA_NET_LSI_UMTS_LEN - SIERRA_NET_LSI_COMMON_LEN) +#define SIERRA_NET_LSI_UMTS_DS_LEN (sizeof(struct lsi_umts_dual)) +#define SIERRA_NET_LSI_UMTS_DS_STATUS_LEN \ + (SIERRA_NET_LSI_UMTS_DS_LEN - SIERRA_NET_LSI_COMMON_LEN) /* Forward definitions */ static void sierra_sync_timer(unsigned long syncdata); @@ -191,10 +217,11 @@ static inline void sierra_net_set_private(struct usbnet *dev, dev->data[0] = (unsigned long)priv; } -/* is packet IPv4 */ +/* is packet IPv4/IPv6 */ static inline int is_ip(struct sk_buff *skb) { - return skb->protocol == cpu_to_be16(ETH_P_IP); + return skb->protocol == cpu_to_be16(ETH_P_IP) || + skb->protocol == cpu_to_be16(ETH_P_IPV6); } /* @@ -350,49 +377,54 @@ static inline int sierra_net_is_valid_addrlen(u8 len) static int sierra_net_parse_lsi(struct usbnet *dev, char *data, int datalen) { struct lsi_umts *lsi = (struct lsi_umts *)data; + u32 expected_length; - if (datalen < sizeof(struct lsi_umts)) { - netdev_err(dev->net, "%s: Data length %d, exp %Zu\n", - __func__, datalen, - sizeof(struct lsi_umts)); + if (datalen < sizeof(struct lsi_umts_single)) { + netdev_err(dev->net, "%s: Data length %d, exp >= %Zu\n", + __func__, datalen, sizeof(struct lsi_umts_single)); return -1; } - if (lsi->length != cpu_to_be16(SIERRA_NET_LSI_UMTS_STATUS_LEN)) { - netdev_err(dev->net, "%s: LSI_UMTS_STATUS_LEN %d, exp %u\n", - __func__, be16_to_cpu(lsi->length), - (u32)SIERRA_NET_LSI_UMTS_STATUS_LEN); - return -1; + /* Validate the session state */ + if (lsi->session_state == SIERRA_NET_SESSION_IDLE) { + netdev_err(dev->net, "Session idle, 0x%02x\n", + lsi->session_state); + return 0; } /* Validate the protocol - only support UMTS for now */ - if (lsi->protocol != SIERRA_NET_PROTOCOL_UMTS) { + if (lsi->protocol == SIERRA_NET_PROTOCOL_UMTS) { + struct lsi_umts_single *single = (struct lsi_umts_single *)lsi; + + /* Validate the link type */ + if (single->link_type != SIERRA_NET_AS_LINK_TYPE_IPV4 && + single->link_type != SIERRA_NET_AS_LINK_TYPE_IPV6) { + netdev_err(dev->net, "Link type unsupported: 0x%02x\n", + single->link_type); + return -1; + } + expected_length = SIERRA_NET_LSI_UMTS_STATUS_LEN; + } else if (lsi->protocol == SIERRA_NET_PROTOCOL_UMTS_DS) { + expected_length = SIERRA_NET_LSI_UMTS_DS_STATUS_LEN; + } else { netdev_err(dev->net, "Protocol unsupported, 0x%02x\n", - lsi->protocol); + lsi->protocol); return -1; } - /* Validate the link type */ - if (lsi->link_type != SIERRA_NET_AS_LINK_TYPE_IPv4) { - netdev_err(dev->net, "Link type unsupported: 0x%02x\n", - lsi->link_type); + if (be16_to_cpu(lsi->length) != expected_length) { + netdev_err(dev->net, "%s: LSI_UMTS_STATUS_LEN %d, exp %u\n", + __func__, be16_to_cpu(lsi->length), expected_length); return -1; } /* Validate the coverage */ - if (lsi->coverage == SIERRA_NET_COVERAGE_NONE - || lsi->coverage == SIERRA_NET_COVERAGE_NOPACKET) { + if (lsi->coverage == SIERRA_NET_COVERAGE_NONE || + lsi->coverage == SIERRA_NET_COVERAGE_NOPACKET) { netdev_err(dev->net, "No coverage, 0x%02x\n", lsi->coverage); return 0; } - /* Validate the session state */ - if (lsi->session_state == SIERRA_NET_SESSION_IDLE) { - netdev_err(dev->net, "Session idle, 0x%02x\n", - lsi->session_state); - return 0; - } - /* Set link_sense true */ return 1; } @@ -662,7 +694,6 @@ static int sierra_net_bind(struct usbnet *dev, struct usb_interface *intf) u8 numendpoints; u16 fwattr = 0; int status; - struct ethhdr *eth; struct sierra_net_data *priv; static const u8 sync_tmplate[sizeof(priv->sync_msg)] = { 0x00, 0x00, SIERRA_NET_HIP_MSYNC_ID, 0x00}; @@ -700,11 +731,6 @@ static int sierra_net_bind(struct usbnet *dev, struct usb_interface *intf) dev->net->dev_addr[ETH_ALEN-2] = atomic_inc_return(&iface_counter); dev->net->dev_addr[ETH_ALEN-1] = ifacenum; - /* we will have to manufacture ethernet headers, prepare template */ - eth = (struct ethhdr *)priv->ethr_hdr_tmpl; - memcpy(ð->h_dest, dev->net->dev_addr, ETH_ALEN); - eth->h_proto = cpu_to_be16(ETH_P_IP); - /* prepare shutdown message template */ memcpy(priv->shdwn_msg, shdwn_tmplate, sizeof(priv->shdwn_msg)); /* set context index initially to 0 - prepares tx hdr template */ @@ -833,9 +859,14 @@ static int sierra_net_rx_fixup(struct usbnet *dev, struct sk_buff *skb) skb_pull(skb, hh.hdrlen); - /* We are going to accept this packet, prepare it */ - memcpy(skb->data, sierra_net_get_private(dev)->ethr_hdr_tmpl, - ETH_HLEN); + /* We are going to accept this packet, prepare it. + * In case protocol is IPv6, keep it, otherwise force IPv4. + */ + skb_reset_mac_header(skb); + if (eth_hdr(skb)->h_proto != cpu_to_be16(ETH_P_IPV6)) + eth_hdr(skb)->h_proto = cpu_to_be16(ETH_P_IP); + eth_zero_addr(eth_hdr(skb)->h_source); + memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN); /* Last packet in batch handled by usbnet */ if (hh.payload_len.word == skb->len) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 0e2a19e58923..7f7c87762bc6 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1415,6 +1415,7 @@ static const struct net_device_ops virtnet_netdev = { #ifdef CONFIG_NET_RX_BUSY_POLL .ndo_busy_poll = virtnet_busy_poll, #endif + .ndo_features_check = passthru_features_check, }; static void virtnet_config_changed_work(struct work_struct *work) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 590750ab6564..9a986ccd42e5 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -77,6 +77,8 @@ static const u8 all_zeros_mac[ETH_ALEN]; static int vxlan_sock_add(struct vxlan_dev *vxlan); +static void vxlan_vs_del_dev(struct vxlan_dev *vxlan); + /* per-network namespace private data for this module */ struct vxlan_net { struct list_head vxlan_list; @@ -1052,6 +1054,8 @@ static void __vxlan_sock_release(struct vxlan_sock *vs) static void vxlan_sock_release(struct vxlan_dev *vxlan) { + vxlan_vs_del_dev(vxlan); + __vxlan_sock_release(vxlan->vn4_sock); #if IS_ENABLED(CONFIG_IPV6) __vxlan_sock_release(vxlan->vn6_sock); @@ -2255,6 +2259,15 @@ static void vxlan_cleanup(unsigned long arg) mod_timer(&vxlan->age_timer, next_timer); } +static void vxlan_vs_del_dev(struct vxlan_dev *vxlan) +{ + struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); + + spin_lock(&vn->sock_lock); + hlist_del_init_rcu(&vxlan->hlist); + spin_unlock(&vn->sock_lock); +} + static void vxlan_vs_add_dev(struct vxlan_sock *vs, struct vxlan_dev *vxlan) { struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); @@ -3028,12 +3041,6 @@ static int vxlan_newlink(struct net *src_net, struct net_device *dev, static void vxlan_dellink(struct net_device *dev, struct list_head *head) { struct vxlan_dev *vxlan = netdev_priv(dev); - struct vxlan_net *vn = net_generic(vxlan->net, vxlan_net_id); - - spin_lock(&vn->sock_lock); - if (!hlist_unhashed(&vxlan->hlist)) - hlist_del_rcu(&vxlan->hlist); - spin_unlock(&vn->sock_lock); gro_cells_destroy(&vxlan->gro_cells); list_del(&vxlan->next); diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 486af5dac5df..2217e0cae4da 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -333,5 +333,6 @@ source "drivers/net/wireless/cw1200/Kconfig" source "drivers/net/wireless/rsi/Kconfig" source "drivers/net/wireless/cnss/Kconfig" source "drivers/net/wireless/cnss_genl/Kconfig" +source "drivers/net/wireless/cnss_utils/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 0204fc00f0c5..704a976bdfb5 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -67,3 +67,4 @@ obj-$(CONFIG_CNSS) += cnss/ obj-$(CONFIG_WCNSS_MEM_PRE_ALLOC) += cnss_prealloc/ obj-$(CONFIG_CNSS_CRYPTO) += cnss_crypto/ obj-$(CONFIG_CNSS_GENL) += cnss_genl/ +obj-$(CONFIG_CNSS_UTILS) += cnss_utils/ diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 9acaffa51516..6906bddb229f 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1550,6 +1550,7 @@ static void ath10k_core_restart(struct work_struct *work) complete(&ar->offchan_tx_completed); complete(&ar->install_key_done); complete(&ar->vdev_setup_done); + complete(&ar->vdev_delete_done); complete(&ar->thermal.wmi_sync); complete(&ar->bss_survey_done); wake_up(&ar->htt.empty_tx_wq); @@ -2373,8 +2374,10 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, init_completion(&ar->install_key_done); init_completion(&ar->vdev_setup_done); + init_completion(&ar->vdev_delete_done); init_completion(&ar->thermal.wmi_sync); init_completion(&ar->bss_survey_done); + init_completion(&ar->peer_delete_done); INIT_DELAYED_WORK(&ar->scan.timeout, ath10k_scan_timeout_work); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 01d5ecc4f6b8..8ddbae96794d 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -365,7 +365,8 @@ struct ath10k_sta { #endif }; -#define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5 * HZ) +#define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5 * HZ) +#define ATH10K_VDEV_DELETE_TIMEOUT_HZ (5 * HZ) enum ath10k_beacon_state { ATH10K_BEACON_SCHEDULED = 0, @@ -853,6 +854,7 @@ struct ath10k { struct completion install_key_done; struct completion vdev_setup_done; + struct completion vdev_delete_done; struct workqueue_struct *workqueue; /* Auxiliary workqueue */ @@ -957,7 +959,9 @@ struct ath10k { struct fw_flag *fw_flags; /* set for bmi chip sets */ + struct completion peer_delete_done; bool is_bmi; + enum ieee80211_sta_state sta_state; /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); }; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index b9d08b4b4cc5..e430bab869a8 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -782,6 +782,7 @@ static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value) static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) { int ret; + unsigned long time_left; lockdep_assert_held(&ar->conf_mutex); @@ -793,6 +794,16 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) if (ret) return ret; + if (QCA_REV_WCN3990(ar)) { + time_left = wait_for_completion_timeout(&ar->peer_delete_done, + 50 * HZ); + + if (time_left == 0) { + ath10k_warn(ar, "Timeout in receiving peer delete response\n"); + return -ETIMEDOUT; + } + } + ar->num_peers--; return 0; @@ -983,6 +994,7 @@ static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id) arg.channel.max_antenna_gain = channel->max_antenna_gain * 2; reinit_completion(&ar->vdev_setup_done); + reinit_completion(&ar->vdev_delete_done); ret = ath10k_wmi_vdev_start(ar, &arg); if (ret) { @@ -1032,6 +1044,7 @@ static int ath10k_monitor_vdev_stop(struct ath10k *ar) ar->monitor_vdev_id, ret); reinit_completion(&ar->vdev_setup_done); + reinit_completion(&ar->vdev_delete_done); ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret) @@ -1338,6 +1351,7 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif) lockdep_assert_held(&ar->conf_mutex); reinit_completion(&ar->vdev_setup_done); + reinit_completion(&ar->vdev_delete_done); ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id); if (ret) { @@ -1374,6 +1388,7 @@ static int ath10k_vdev_start_restart(struct ath10k_vif *arvif, lockdep_assert_held(&ar->conf_mutex); reinit_completion(&ar->vdev_setup_done); + reinit_completion(&ar->vdev_delete_done); arg.vdev_id = arvif->vdev_id; arg.dtim_period = arvif->dtim_period; @@ -5112,6 +5127,7 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); struct ath10k_peer *peer; + unsigned long time_left; int ret; int i; @@ -5151,6 +5167,16 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, ath10k_warn(ar, "failed to delete WMI vdev %i: %d\n", arvif->vdev_id, ret); + if (QCA_REV_WCN3990(ar)) { + time_left = wait_for_completion_timeout( + &ar->vdev_delete_done, + ATH10K_VDEV_DELETE_TIMEOUT_HZ); + if (time_left == 0) { + ath10k_warn(ar, "Timeout in receiving vdev delete resp\n"); + return; + } + } + /* Some firmware revisions don't notify host about self-peer removal * until after associated vdev is deleted. */ @@ -5204,6 +5230,26 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, mutex_unlock(&ar->conf_mutex); } +static int ath10k_change_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, bool p2p) +{ + struct ath10k *ar = hw->priv; + int ret = 0; + + ath10k_dbg(ar, ATH10K_DBG_MAC, + "change_interface new: %d (%d), old: %d (%d)\n", new_type, + p2p, vif->type, vif->p2p); + + if (new_type != vif->type || vif->p2p != p2p) { + ath10k_remove_interface(hw, vif); + vif->type = new_type; + vif->p2p = p2p; + ret = ath10k_add_interface(hw, vif); + } + return ret; +} + /* * FIXME: Has to be verified. */ @@ -5924,6 +5970,9 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, new_state == IEEE80211_STA_NOTEXIST)) cancel_work_sync(&arsta->update_wk); + if (vif->type == NL80211_IFTYPE_STATION && new_state > ar->sta_state) + ar->sta_state = new_state; + mutex_lock(&ar->conf_mutex); if (old_state == IEEE80211_STA_NOTEXIST && @@ -6584,7 +6633,8 @@ static int ath10k_get_survey(struct ieee80211_hw *hw, int idx, goto exit; } - ath10k_mac_update_bss_chan_survey(ar, &sband->channels[idx]); + if (!QCA_REV_WCN3990(ar)) + ath10k_mac_update_bss_chan_survey(ar, &sband->channels[idx]); spin_lock_bh(&ar->data_lock); memcpy(survey, ar_survey, sizeof(*survey)); @@ -7392,8 +7442,9 @@ ath10k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw, ctx, arvif->vdev_id); WARN_ON(!arvif->is_started); - - if (vif->type == NL80211_IFTYPE_MONITOR) { + if (vif->type == NL80211_IFTYPE_MONITOR || + (vif->type == NL80211_IFTYPE_STATION && + ar->sta_state < IEEE80211_STA_ASSOC)) { WARN_ON(!arvif->is_up); ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); @@ -7409,6 +7460,7 @@ ath10k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw, ath10k_warn(ar, "failed to stop vdev %i: %d\n", arvif->vdev_id, ret); + ar->sta_state = IEEE80211_STA_NOTEXIST; arvif->is_started = false; mutex_unlock(&ar->conf_mutex); @@ -7440,6 +7492,7 @@ static const struct ieee80211_ops ath10k_ops = { .stop = ath10k_stop, .config = ath10k_config, .add_interface = ath10k_add_interface, + .change_interface = ath10k_change_interface, .remove_interface = ath10k_remove_interface, .configure_filter = ath10k_configure_filter, .bss_info_changed = ath10k_bss_info_changed, @@ -7736,6 +7789,81 @@ static struct ieee80211_iface_combination ath10k_tlv_qcs_if_comb[] = { }, }; +static const struct ieee80211_iface_limit ath10k_wcn3990_if_limit[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 2, + .types = BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_DEVICE), + }, +}; + +static const struct ieee80211_iface_limit ath10k_wcn3990_qcs_if_limit[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 2, + .types = BIT(NL80211_IFTYPE_P2P_CLIENT), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_P2P_GO), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_DEVICE), + }, +}; + +static const struct ieee80211_iface_limit ath10k_wcn3990_if_limit_ibss[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_ADHOC), + }, +}; + +static struct ieee80211_iface_combination ath10k_wcn3990_qcs_if_comb[] = { + { + .limits = ath10k_wcn3990_if_limit, + .num_different_channels = 1, + .max_interfaces = 4, + .n_limits = ARRAY_SIZE(ath10k_wcn3990_if_limit), + }, + { + .limits = ath10k_wcn3990_qcs_if_limit, + .num_different_channels = 2, + .max_interfaces = 4, + .n_limits = ARRAY_SIZE(ath10k_wcn3990_qcs_if_limit), + }, + { + .limits = ath10k_wcn3990_if_limit_ibss, + .num_different_channels = 1, + .max_interfaces = 2, + .n_limits = ARRAY_SIZE(ath10k_wcn3990_if_limit_ibss), + }, +}; + static const struct ieee80211_iface_limit ath10k_10_4_if_limits[] = { { .max = 1, @@ -7967,6 +8095,14 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); break; case ATH10K_FW_WMI_OP_VERSION_TLV: + ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); + if (QCA_REV_WCN3990(ar)) { + ar->hw->wiphy->iface_combinations = + ath10k_wcn3990_qcs_if_comb; + ar->hw->wiphy->n_iface_combinations = + ARRAY_SIZE(ath10k_wcn3990_qcs_if_comb); + break; + } if (test_bit(WMI_SERVICE_ADAPTIVE_OCS, ar->wmi.svc_map)) { ar->hw->wiphy->iface_combinations = ath10k_tlv_qcs_if_comb; @@ -7977,7 +8113,6 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath10k_tlv_if_comb); } - ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); break; case ATH10K_FW_WMI_OP_VERSION_10_1: case ATH10K_FW_WMI_OP_VERSION_10_2: diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 75f2528b8b84..888ab5ad37f1 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -412,6 +412,15 @@ static int ath10k_wmi_tlv_event_tx_pause(struct ath10k *ar, return 0; } +static int ath10k_wmi_tlv_event_peer_delete_resp(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TLV_PEER_DELETE_RESP_EVENTID\n"); + complete(&ar->peer_delete_done); + + return 0; +} + /***********/ /* TLV ops */ /***********/ @@ -468,6 +477,9 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb) case WMI_TLV_VDEV_STOPPED_EVENTID: ath10k_wmi_event_vdev_stopped(ar, skb); break; + case WMI_TLV_VDEV_DELETE_RESP_EVENTID: + ath10k_wmi_event_vdev_delete_resp(ar, skb); + break; case WMI_TLV_PEER_STA_KICKOUT_EVENTID: ath10k_wmi_event_peer_sta_kickout(ar, skb); break; @@ -552,6 +564,9 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb) case WMI_TLV_TX_PAUSE_EVENTID: ath10k_wmi_tlv_event_tx_pause(ar, skb); break; + case WMI_TLV_PEER_DELETE_RESP_EVENTID: + ath10k_wmi_tlv_event_peer_delete_resp(ar, skb); + break; default: ath10k_dbg(ar, ATH10K_DBG_WMI, "Unknown eventid: %d\n", id); break; @@ -669,6 +684,13 @@ static int ath10k_wmi_tlv_op_pull_ch_info_ev(struct ath10k *ar, arg->noise_floor = ev->noise_floor; arg->rx_clear_count = ev->rx_clear_count; arg->cycle_count = ev->cycle_count; + arg->chan_tx_pwr_range = ev->chan_tx_pwr_range; + arg->chan_tx_pwr_tp = ev->chan_tx_pwr_tp; + arg->rx_frame_count = ev->rx_frame_count; + arg->my_bss_rx_cycle_count = ev->my_bss_rx_cycle_count; + arg->rx_11b_mode_data_duration = ev->rx_11b_mode_data_duration; + arg->tx_frame_cnt = ev->tx_frame_cnt; + arg->mac_clk_mhz = ev->mac_clk_mhz; kfree(tb); return 0; @@ -1512,11 +1534,14 @@ ath10k_wmi_tlv_op_gen_start_scan(struct ath10k *ar, cmd->ie_len = __cpu_to_le32(arg->ie_len); cmd->num_probes = __cpu_to_le32(3); - if (QCA_REV_WCN3990(ar)) + if (QCA_REV_WCN3990(ar)) { cmd->common.scan_ctrl_flags = ar->fw_flags->flags; - else + cmd->common.scan_ctrl_flags |= + __cpu_to_le32(WMI_SCAN_CHAN_STAT_EVENT); + } else { cmd->common.scan_ctrl_flags ^= __cpu_to_le32(WMI_SCAN_FILTER_PROBE_REQ); + } ptr += sizeof(*tlv); ptr += sizeof(*cmd); diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h index 79f324f132e9..86b24c24d9f1 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -308,11 +308,15 @@ enum wmi_tlv_event_id { WMI_TLV_VDEV_STOPPED_EVENTID, WMI_TLV_VDEV_INSTALL_KEY_COMPLETE_EVENTID, WMI_TLV_VDEV_MCC_BCN_INTERVAL_CHANGE_REQ_EVENTID, + WMI_TLV_VDEV_TSF_REPORT_EVENTID, + WMI_TLV_VDEV_DELETE_RESP_EVENTID, WMI_TLV_PEER_STA_KICKOUT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_PEER), WMI_TLV_PEER_INFO_EVENTID, WMI_TLV_PEER_TX_FAIL_CNT_THR_EVENTID, WMI_TLV_PEER_ESTIMATED_LINKSPEED_EVENTID, WMI_TLV_PEER_STATE_EVENTID, + WMI_TLV_PEER_ASSOC_CONF_EVENTID, + WMI_TLV_PEER_DELETE_RESP_EVENTID, WMI_TLV_MGMT_RX_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MGMT), WMI_TLV_HOST_SWBA_EVENTID, WMI_TLV_TBTTOFFSET_UPDATE_EVENTID, diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index cbbba8d79e6b..d60e7fbb7e74 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2446,6 +2446,31 @@ static int ath10k_wmi_10_4_op_pull_ch_info_ev(struct ath10k *ar, return 0; } +static void wlan_fill_survey_result(struct ath10k *ar, + struct survey_info *survey, + struct wmi_ch_info_ev_arg arg) +{ + u64 clock_freq; + + if (!arg.mac_clk_mhz || !survey) + return; + + clock_freq = arg.mac_clk_mhz * 1000; + + memset(survey, 0, sizeof(*survey)); + + survey->noise = __le32_to_cpu(arg.noise_floor); + survey->time = __le32_to_cpu(arg.cycle_count) / clock_freq; + survey->time_busy = __le32_to_cpu(arg.rx_clear_count) / clock_freq; + survey->time_tx = __le32_to_cpu(arg.rx_clear_count) / clock_freq; + + survey->filled = SURVEY_INFO_NOISE_DBM; + ar->ch_info_can_report_survey = true; + + survey->filled |= (SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_TX); +} + void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) { struct wmi_ch_info_ev_arg arg = {}; @@ -2490,6 +2515,12 @@ void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) goto exit; } + if (QCA_REV_WCN3990(ar)) { + survey = &ar->survey[idx]; + wlan_fill_survey_result(ar, survey, arg); + goto exit; + } + if (cmd_flags & WMI_CHAN_INFO_FLAG_COMPLETE) { if (ar->ch_info_can_report_survey) { survey = &ar->survey[idx]; @@ -3120,6 +3151,12 @@ void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, struct sk_buff *skb) complete(&ar->vdev_setup_done); } +void ath10k_wmi_event_vdev_delete_resp(struct ath10k *ar, struct sk_buff *skb) +{ + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_DELETE_RESP_EVENTID\n"); + complete(&ar->vdev_delete_done); +} + static int ath10k_wmi_op_pull_peer_kick_ev(struct ath10k *ar, struct sk_buff *skb, struct wmi_peer_kick_ev_arg *arg) diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 74398b45e365..9bd374910379 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -6034,6 +6034,13 @@ struct wmi_chan_info_event { __le32 noise_floor; __le32 rx_clear_count; __le32 cycle_count; + __le32 chan_tx_pwr_range; + __le32 chan_tx_pwr_tp; + __le32 rx_frame_count; + __le32 my_bss_rx_cycle_count; + __le32 rx_11b_mode_data_duration; + __le32 tx_frame_cnt; + __le32 mac_clk_mhz; } __packed; struct wmi_10_4_chan_info_event { @@ -6247,6 +6254,10 @@ struct wmi_ch_info_ev_arg { __le32 chan_tx_pwr_range; __le32 chan_tx_pwr_tp; __le32 rx_frame_count; + __le32 my_bss_rx_cycle_count; + __le32 rx_11b_mode_data_duration; + __le32 tx_frame_cnt; + __le32 mac_clk_mhz; }; struct wmi_vdev_start_ev_arg { @@ -6599,6 +6610,7 @@ int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb); void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb); void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, struct sk_buff *skb); void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_vdev_delete_resp(struct ath10k *ar, struct sk_buff *skb); void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct sk_buff *skb); void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb); void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, struct sk_buff *skb); diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 165dd202c365..c92564b3ec85 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -37,6 +37,7 @@ static struct usb_device_id ath9k_hif_usb_ids[] = { { USB_DEVICE(0x0cf3, 0xb002) }, /* Ubiquiti WifiStation */ { USB_DEVICE(0x057c, 0x8403) }, /* AVM FRITZ!WLAN 11N v2 USB */ { USB_DEVICE(0x0471, 0x209e) }, /* Philips (or NXP) PTA01 */ + { USB_DEVICE(0x1eda, 0x2315) }, /* AirTies */ { USB_DEVICE(0x0cf3, 0x7015), .driver_info = AR9287_USB }, /* Atheros */ @@ -1216,6 +1217,9 @@ static int send_eject_command(struct usb_interface *interface) u8 bulk_out_ep; int r; + if (iface_desc->desc.bNumEndpoints < 2) + return -ENODEV; + /* Find bulk out endpoint */ for (r = 1; r >= 0; r--) { endpoint = &iface_desc->endpoint[r].desc; diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 06ea6cc9e30a..096818610d40 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -38,28 +38,28 @@ static int __ath_regd_init(struct ath_regulatory *reg); /* We enable active scan on these a case by case basis by regulatory domain */ #define ATH9K_2GHZ_CH12_13 REG_RULE(2467-10, 2472+10, 40, 0, 20,\ NL80211_RRF_NO_IR) -#define ATH9K_2GHZ_CH14 REG_RULE(2484-10, 2484+10, 40, 0, 20,\ +#define ATH9K_2GHZ_CH14 REG_RULE(2484 - 10, 2484 + 10, 20, 0, 20,\ NL80211_RRF_NO_IR | \ NL80211_RRF_NO_OFDM) /* We allow IBSS on these on a case by case basis by regulatory domain */ -#define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 80, 0, 30,\ +#define ATH9K_5GHZ_5180_5320 REG_RULE(5180 - 10, 5320 + 10, 160, 0, 20,\ NL80211_RRF_NO_IR) -#define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 80, 0, 30,\ +#define ATH9K_5GHZ_5500_5825 REG_RULE(5500 - 10, 5825 + 10, 80, 0, 20,\ NL80211_RRF_NO_IR) -#define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 80, 0, 30,\ +#define ATH9K_5GHZ_5745_5825 REG_RULE(5745 - 10, 5825 + 10, 80, 0, 20,\ NL80211_RRF_NO_IR) #define ATH9K_2GHZ_ALL ATH9K_2GHZ_CH01_11, \ ATH9K_2GHZ_CH12_13, \ ATH9K_2GHZ_CH14 -#define ATH9K_5GHZ_ALL ATH9K_5GHZ_5150_5350, \ - ATH9K_5GHZ_5470_5850 +#define ATH9K_5GHZ_ALL ATH9K_5GHZ_5180_5320, \ + ATH9K_5GHZ_5500_5825 /* This one skips what we call "mid band" */ -#define ATH9K_5GHZ_NO_MIDBAND ATH9K_5GHZ_5150_5350, \ - ATH9K_5GHZ_5725_5850 +#define ATH9K_5GHZ_NO_MIDBAND ATH9K_5GHZ_5180_5320, \ + ATH9K_5GHZ_5745_5825 /* Can be used for: * 0x60, 0x61, 0x62 */ @@ -256,7 +256,7 @@ EXPORT_SYMBOL(ath_is_49ghz_allowed); /* Frequency is one where radar detection is required */ static bool ath_is_radar_freq(u16 center_freq) { - return (center_freq >= 5260 && center_freq <= 5700); + return (center_freq >= 5260 && center_freq <= 5720); } static void ath_force_clear_no_ir_chan(struct wiphy *wiphy, diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 69214fb2586c..63bb7686b811 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -16,6 +16,7 @@ #include <linux/etherdevice.h> #include <linux/moduleparam.h> +#include <net/netlink.h> #include "wil6210.h" #include "wmi.h" #include "ftm.h" @@ -56,6 +57,62 @@ static struct ieee80211_channel wil_60ghz_channels[] = { #define QCA_NL80211_VENDOR_ID 0x001374 +#define WIL_MAX_RF_SECTORS (128) +#define WIL_CID_ALL (0xff) + +enum qca_wlan_vendor_attr_rf_sector { + QCA_ATTR_MAC_ADDR = 6, + QCA_ATTR_PAD = 13, + QCA_ATTR_TSF = 29, + QCA_ATTR_DMG_RF_SECTOR_INDEX = 30, + QCA_ATTR_DMG_RF_SECTOR_TYPE = 31, + QCA_ATTR_DMG_RF_MODULE_MASK = 32, + QCA_ATTR_DMG_RF_SECTOR_CFG = 33, + QCA_ATTR_DMG_RF_SECTOR_MAX, +}; + +enum qca_wlan_vendor_attr_dmg_rf_sector_type { + QCA_ATTR_DMG_RF_SECTOR_TYPE_RX, + QCA_ATTR_DMG_RF_SECTOR_TYPE_TX, + QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX +}; + +enum qca_wlan_vendor_attr_dmg_rf_sector_cfg { + QCA_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0, + QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX, + QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0, + QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1, + QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2, + QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI, + QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO, + QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16, + + /* keep last */ + QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST, + QCA_ATTR_DMG_RF_SECTOR_CFG_MAX = + QCA_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1 +}; + +static const struct +nla_policy wil_rf_sector_policy[QCA_ATTR_DMG_RF_SECTOR_MAX + 1] = { + [QCA_ATTR_MAC_ADDR] = { .len = ETH_ALEN }, + [QCA_ATTR_DMG_RF_SECTOR_INDEX] = { .type = NLA_U16 }, + [QCA_ATTR_DMG_RF_SECTOR_TYPE] = { .type = NLA_U8 }, + [QCA_ATTR_DMG_RF_MODULE_MASK] = { .type = NLA_U32 }, + [QCA_ATTR_DMG_RF_SECTOR_CFG] = { .type = NLA_NESTED }, +}; + +static const struct +nla_policy wil_rf_sector_cfg_policy[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1] = { + [QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] = { .type = NLA_U8 }, + [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] = { .type = NLA_U32 }, + [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] = { .type = NLA_U32 }, + [QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] = { .type = NLA_U32 }, + [QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] = { .type = NLA_U32 }, + [QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] = { .type = NLA_U32 }, + [QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16] = { .type = NLA_U32 }, +}; + enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA = 128, QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION = 129, @@ -66,8 +123,25 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS = 134, QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS = 135, QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136, + QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139, + QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140, + QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141, + QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142, }; +static int wil_rf_sector_get_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); +static int wil_rf_sector_set_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); +static int wil_rf_sector_get_selected(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); +static int wil_rf_sector_set_selected(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len); + /* vendor specific commands */ static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = { { @@ -112,6 +186,36 @@ static const struct wiphy_vendor_command wil_nl80211_vendor_commands[] = { WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = wil_aoa_abort_measurement }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wil_rf_sector_get_cfg + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wil_rf_sector_set_cfg + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = + QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wil_rf_sector_get_selected + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = + QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wil_rf_sector_set_selected + }, }; /* vendor specific events */ @@ -856,6 +960,9 @@ int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, wil_hex_dump_misc("mgmt tx frame ", DUMP_PREFIX_OFFSET, 16, 1, buf, len, true); + if (len < sizeof(struct ieee80211_mgmt)) + return -EINVAL; + cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL); if (!cmd) { rc = -ENOMEM; @@ -1838,3 +1945,449 @@ void wil_p2p_wdev_free(struct wil6210_priv *wil) kfree(p2p_wdev); } } + +static int wil_rf_sector_status_to_rc(u8 status) +{ + switch (status) { + case WMI_RF_SECTOR_STATUS_SUCCESS: + return 0; + case WMI_RF_SECTOR_STATUS_BAD_PARAMETERS_ERROR: + return -EINVAL; + case WMI_RF_SECTOR_STATUS_BUSY_ERROR: + return -EAGAIN; + case WMI_RF_SECTOR_STATUS_NOT_SUPPORTED_ERROR: + return -EOPNOTSUPP; + default: + return -EINVAL; + } +} + +static int wil_rf_sector_get_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct wil6210_priv *wil = wdev_to_wil(wdev); + int rc; + struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; + u16 sector_index; + u8 sector_type; + u32 rf_modules_vec; + struct wmi_get_rf_sector_params_cmd cmd; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_get_rf_sector_params_done_event evt; + } __packed reply; + struct sk_buff *msg; + struct nlattr *nl_cfgs, *nl_cfg; + u32 i; + struct wmi_rf_sector_info *si; + + if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) + return -EOPNOTSUPP; + + rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, + wil_rf_sector_policy); + if (rc) { + wil_err(wil, "Invalid rf sector ATTR\n"); + return rc; + } + + if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] || + !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] || + !tb[QCA_ATTR_DMG_RF_MODULE_MASK]) { + wil_err(wil, "Invalid rf sector spec\n"); + return -EINVAL; + } + + sector_index = nla_get_u16( + tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]); + if (sector_index >= WIL_MAX_RF_SECTORS) { + wil_err(wil, "Invalid sector index %d\n", sector_index); + return -EINVAL; + } + + sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); + if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { + wil_err(wil, "Invalid sector type %d\n", sector_type); + return -EINVAL; + } + + rf_modules_vec = nla_get_u32( + tb[QCA_ATTR_DMG_RF_MODULE_MASK]); + if (rf_modules_vec >= BIT(WMI_MAX_RF_MODULES_NUM)) { + wil_err(wil, "Invalid rf module mask 0x%x\n", rf_modules_vec); + return -EINVAL; + } + + cmd.sector_idx = cpu_to_le16(sector_index); + cmd.sector_type = sector_type; + cmd.rf_modules_vec = rf_modules_vec & 0xFF; + memset(&reply, 0, sizeof(reply)); + rc = wmi_call(wil, WMI_GET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd), + WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID, + &reply, sizeof(reply), + 500); + if (rc) + return rc; + if (reply.evt.status) { + wil_err(wil, "get rf sector cfg failed with status %d\n", + reply.evt.status); + return wil_rf_sector_status_to_rc(reply.evt.status); + } + + msg = cfg80211_vendor_cmd_alloc_reply_skb( + wiphy, 64 * WMI_MAX_RF_MODULES_NUM); + if (!msg) + return -ENOMEM; + + if (nla_put_u64(msg, QCA_ATTR_TSF, + le64_to_cpu(reply.evt.tsf))) + goto nla_put_failure; + + nl_cfgs = nla_nest_start(msg, QCA_ATTR_DMG_RF_SECTOR_CFG); + if (!nl_cfgs) + goto nla_put_failure; + for (i = 0; i < WMI_MAX_RF_MODULES_NUM; i++) { + if (!(rf_modules_vec & BIT(i))) + continue; + nl_cfg = nla_nest_start(msg, i); + if (!nl_cfg) + goto nla_put_failure; + si = &reply.evt.sectors_info[i]; + if (nla_put_u8(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX, + i) || + nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0, + le32_to_cpu(si->etype0)) || + nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1, + le32_to_cpu(si->etype1)) || + nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2, + le32_to_cpu(si->etype2)) || + nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI, + le32_to_cpu(si->psh_hi)) || + nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO, + le32_to_cpu(si->psh_lo)) || + nla_put_u32(msg, QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16, + le32_to_cpu(si->dtype_swch_off))) + goto nla_put_failure; + nla_nest_end(msg, nl_cfg); + } + + nla_nest_end(msg, nl_cfgs); + rc = cfg80211_vendor_cmd_reply(msg); + return rc; +nla_put_failure: + kfree_skb(msg); + return -ENOBUFS; +} + +static int wil_rf_sector_set_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct wil6210_priv *wil = wdev_to_wil(wdev); + int rc, tmp; + struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; + struct nlattr *tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MAX + 1]; + u16 sector_index, rf_module_index; + u8 sector_type; + u32 rf_modules_vec = 0; + struct wmi_set_rf_sector_params_cmd cmd; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_set_rf_sector_params_done_event evt; + } __packed reply; + struct nlattr *nl_cfg; + struct wmi_rf_sector_info *si; + + if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) + return -EOPNOTSUPP; + + rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, + wil_rf_sector_policy); + if (rc) { + wil_err(wil, "Invalid rf sector ATTR\n"); + return rc; + } + + if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] || + !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE] || + !tb[QCA_ATTR_DMG_RF_SECTOR_CFG]) { + wil_err(wil, "Invalid rf sector spec\n"); + return -EINVAL; + } + + sector_index = nla_get_u16( + tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]); + if (sector_index >= WIL_MAX_RF_SECTORS) { + wil_err(wil, "Invalid sector index %d\n", sector_index); + return -EINVAL; + } + + sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); + if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { + wil_err(wil, "Invalid sector type %d\n", sector_type); + return -EINVAL; + } + + memset(&cmd, 0, sizeof(cmd)); + + cmd.sector_idx = cpu_to_le16(sector_index); + cmd.sector_type = sector_type; + nla_for_each_nested(nl_cfg, tb[QCA_ATTR_DMG_RF_SECTOR_CFG], + tmp) { + rc = nla_parse_nested(tb2, QCA_ATTR_DMG_RF_SECTOR_CFG_MAX, + nl_cfg, wil_rf_sector_cfg_policy); + if (rc) { + wil_err(wil, "invalid sector cfg\n"); + return -EINVAL; + } + + if (!tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX] || + !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0] || + !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1] || + !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2] || + !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI] || + !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO] || + !tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16]) { + wil_err(wil, "missing cfg params\n"); + return -EINVAL; + } + + rf_module_index = nla_get_u8( + tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX]); + if (rf_module_index >= WMI_MAX_RF_MODULES_NUM) { + wil_err(wil, "invalid RF module index %d\n", + rf_module_index); + return -EINVAL; + } + rf_modules_vec |= BIT(rf_module_index); + si = &cmd.sectors_info[rf_module_index]; + si->etype0 = cpu_to_le32(nla_get_u32( + tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE0])); + si->etype1 = cpu_to_le32(nla_get_u32( + tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE1])); + si->etype2 = cpu_to_le32(nla_get_u32( + tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_ETYPE2])); + si->psh_hi = cpu_to_le32(nla_get_u32( + tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_HI])); + si->psh_lo = cpu_to_le32(nla_get_u32( + tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_PSH_LO])); + si->dtype_swch_off = cpu_to_le32(nla_get_u32( + tb2[QCA_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16])); + } + + cmd.rf_modules_vec = rf_modules_vec & 0xFF; + memset(&reply, 0, sizeof(reply)); + rc = wmi_call(wil, WMI_SET_RF_SECTOR_PARAMS_CMDID, &cmd, sizeof(cmd), + WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID, + &reply, sizeof(reply), + 500); + if (rc) + return rc; + return wil_rf_sector_status_to_rc(reply.evt.status); +} + +static int wil_rf_sector_get_selected(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct wil6210_priv *wil = wdev_to_wil(wdev); + int rc; + struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; + u8 sector_type, mac_addr[ETH_ALEN]; + int cid = 0; + struct wmi_get_selected_rf_sector_index_cmd cmd; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_get_selected_rf_sector_index_done_event evt; + } __packed reply; + struct sk_buff *msg; + + if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) + return -EOPNOTSUPP; + + rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, + wil_rf_sector_policy); + if (rc) { + wil_err(wil, "Invalid rf sector ATTR\n"); + return rc; + } + + if (!tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) { + wil_err(wil, "Invalid rf sector spec\n"); + return -EINVAL; + } + sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); + if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { + wil_err(wil, "Invalid sector type %d\n", sector_type); + return -EINVAL; + } + + if (tb[QCA_ATTR_MAC_ADDR]) { + ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR])); + cid = wil_find_cid(wil, mac_addr); + if (cid < 0) { + wil_err(wil, "invalid MAC address %pM\n", mac_addr); + return -ENOENT; + } + } else { + if (test_bit(wil_status_fwconnected, wil->status)) { + wil_err(wil, "must specify MAC address when connected\n"); + return -EINVAL; + } + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.cid = (u8)cid; + cmd.sector_type = sector_type; + memset(&reply, 0, sizeof(reply)); + rc = wmi_call(wil, WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID, + &cmd, sizeof(cmd), + WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID, + &reply, sizeof(reply), + 500); + if (rc) + return rc; + if (reply.evt.status) { + wil_err(wil, "get rf selected sector cfg failed with status %d\n", + reply.evt.status); + return wil_rf_sector_status_to_rc(reply.evt.status); + } + + msg = cfg80211_vendor_cmd_alloc_reply_skb( + wiphy, 64 * WMI_MAX_RF_MODULES_NUM); + if (!msg) + return -ENOMEM; + + if (nla_put_u64(msg, QCA_ATTR_TSF, + le64_to_cpu(reply.evt.tsf)) || + nla_put_u16(msg, QCA_ATTR_DMG_RF_SECTOR_INDEX, + le16_to_cpu(reply.evt.sector_idx))) + goto nla_put_failure; + + rc = cfg80211_vendor_cmd_reply(msg); + return rc; +nla_put_failure: + kfree_skb(msg); + return -ENOBUFS; +} + +static int wil_rf_sector_wmi_set_selected(struct wil6210_priv *wil, + u16 sector_index, + u8 sector_type, u8 cid) +{ + struct wmi_set_selected_rf_sector_index_cmd cmd; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_set_selected_rf_sector_index_done_event evt; + } __packed reply; + int rc; + + memset(&cmd, 0, sizeof(cmd)); + cmd.sector_idx = cpu_to_le16(sector_index); + cmd.sector_type = sector_type; + cmd.cid = (u8)cid; + memset(&reply, 0, sizeof(reply)); + rc = wmi_call(wil, WMI_SET_SELECTED_RF_SECTOR_INDEX_CMDID, + &cmd, sizeof(cmd), + WMI_SET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID, + &reply, sizeof(reply), + 500); + if (rc) + return rc; + return wil_rf_sector_status_to_rc(reply.evt.status); +} + +static int wil_rf_sector_set_selected(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct wil6210_priv *wil = wdev_to_wil(wdev); + int rc; + struct nlattr *tb[QCA_ATTR_DMG_RF_SECTOR_MAX + 1]; + u16 sector_index; + u8 sector_type, mac_addr[ETH_ALEN], i; + int cid = 0; + + if (!test_bit(WMI_FW_CAPABILITY_RF_SECTORS, wil->fw_capabilities)) + return -EOPNOTSUPP; + + rc = nla_parse(tb, QCA_ATTR_DMG_RF_SECTOR_MAX, data, data_len, + wil_rf_sector_policy); + if (rc) { + wil_err(wil, "Invalid rf sector ATTR\n"); + return rc; + } + + if (!tb[QCA_ATTR_DMG_RF_SECTOR_INDEX] || + !tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]) { + wil_err(wil, "Invalid rf sector spec\n"); + return -EINVAL; + } + + sector_index = nla_get_u16( + tb[QCA_ATTR_DMG_RF_SECTOR_INDEX]); + if (sector_index >= WIL_MAX_RF_SECTORS && + sector_index != WMI_INVALID_RF_SECTOR_INDEX) { + wil_err(wil, "Invalid sector index %d\n", sector_index); + return -EINVAL; + } + + sector_type = nla_get_u8(tb[QCA_ATTR_DMG_RF_SECTOR_TYPE]); + if (sector_type >= QCA_ATTR_DMG_RF_SECTOR_TYPE_MAX) { + wil_err(wil, "Invalid sector type %d\n", sector_type); + return -EINVAL; + } + + if (tb[QCA_ATTR_MAC_ADDR]) { + ether_addr_copy(mac_addr, nla_data(tb[QCA_ATTR_MAC_ADDR])); + if (!is_broadcast_ether_addr(mac_addr)) { + cid = wil_find_cid(wil, mac_addr); + if (cid < 0) { + wil_err(wil, "invalid MAC address %pM\n", + mac_addr); + return -ENOENT; + } + } else { + if (sector_index != WMI_INVALID_RF_SECTOR_INDEX) { + wil_err(wil, "broadcast MAC valid only with unlocking\n"); + return -EINVAL; + } + cid = -1; + } + } else { + if (test_bit(wil_status_fwconnected, wil->status)) { + wil_err(wil, "must specify MAC address when connected\n"); + return -EINVAL; + } + /* otherwise, using cid=0 for unassociated station */ + } + + if (cid >= 0) { + rc = wil_rf_sector_wmi_set_selected(wil, sector_index, + sector_type, cid); + } else { + /* unlock all cids */ + rc = wil_rf_sector_wmi_set_selected( + wil, WMI_INVALID_RF_SECTOR_INDEX, sector_type, + WIL_CID_ALL); + if (rc == -EINVAL) { + for (i = 0; i < WIL6210_MAX_CID; i++) { + rc = wil_rf_sector_wmi_set_selected( + wil, WMI_INVALID_RF_SECTOR_INDEX, + sector_type, i); + /* the FW will silently ignore and return + * success for unused cid, so abort the loop + * on any other error + */ + if (rc) { + wil_err(wil, "unlock cid %d failed with status %d\n", + i, rc); + break; + } + } + } + } + + return rc; +} diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index d3691c64d47a..6eefb9e61ec4 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -805,8 +805,12 @@ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf, struct wireless_dev *wdev = wil_to_wdev(wil); struct cfg80211_mgmt_tx_params params; int rc; - void *frame = kmalloc(len, GFP_KERNEL); + void *frame; + if (!len) + return -EINVAL; + + frame = kmalloc(len, GFP_KERNEL); if (!frame) return -ENOMEM; diff --git a/drivers/net/wireless/ath/wil6210/ioctl.c b/drivers/net/wireless/ath/wil6210/ioctl.c index bbdd232df3b7..f8d2c209482c 100644 --- a/drivers/net/wireless/ath/wil6210/ioctl.c +++ b/drivers/net/wireless/ath/wil6210/ioctl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Qualcomm Atheros, Inc. + * Copyright (c) 2014,2017 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -54,7 +54,7 @@ static void __iomem *wil_ioc_addr(struct wil6210_priv *wil, uint32_t addr, } off = a - wil->csr; - if (size >= WIL6210_MEM_SIZE - off) { + if (size >= wil->bar_size - off) { wil_err(wil, "Requested block does not fit into memory: " "off = 0x%08x size = 0x%08x\n", off, size); return NULL; diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 8d3d25333ba5..e91cddbacf24 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -198,16 +198,18 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) .ramdump = wil_platform_rop_ramdump, .fw_recovery = wil_platform_rop_fw_recovery, }; + u32 bar_size = pci_resource_len(pdev, 0); /* check HW */ dev_info(&pdev->dev, WIL_NAME - " device found [%04x:%04x] (rev %x)\n", - (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); - - if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) { - dev_err(&pdev->dev, "Not " WIL_NAME "? " - "BAR0 size is %lu while expecting %lu\n", - (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE); + " device found [%04x:%04x] (rev %x) bar size 0x%x\n", + (int)pdev->vendor, (int)pdev->device, (int)pdev->revision, + bar_size); + + if ((bar_size < WIL6210_MIN_MEM_SIZE) || + (bar_size > WIL6210_MAX_MEM_SIZE)) { + dev_err(&pdev->dev, "Unexpected BAR0 size 0x%x\n", + bar_size); return -ENODEV; } @@ -220,6 +222,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) wil->pdev = pdev; pci_set_drvdata(pdev, wil); + wil->bar_size = bar_size; /* rollback to if_free */ wil->platform_handle = diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c index b4faf8212348..ce1f384e7f8e 100644 --- a/drivers/net/wireless/ath/wil6210/pm.c +++ b/drivers/net/wireless/ath/wil6210/pm.c @@ -98,6 +98,10 @@ static int wil_resume_keep_radio_on(struct wil6210_priv *wil) } } + /* Wake all queues */ + if (test_bit(wil_status_fwconnected, wil->status)) + wil_update_net_queues_bh(wil, NULL, false); + out: if (rc) set_bit(wil_status_suspended, wil->status); @@ -113,6 +117,7 @@ static int wil_suspend_keep_radio_on(struct wil6210_priv *wil) /* Prevent handling of new tx and wmi commands */ set_bit(wil_status_suspending, wil->status); + wil_update_net_queues_bh(wil, NULL, true); if (!wil_is_tx_idle(wil)) { wil_dbg_pm(wil, "Pending TX data, reject suspend\n"); @@ -201,13 +206,17 @@ resume_after_fail: clear_bit(wil_status_suspending, wil->status); rc = wmi_resume(wil); /* if resume succeeded, reject the suspend */ - if (!rc) + if (!rc) { rc = -EBUSY; - + if (test_bit(wil_status_fwconnected, wil->status)) + wil_update_net_queues_bh(wil, NULL, false); + } return rc; reject_suspend: clear_bit(wil_status_suspending, wil->status); + if (test_bit(wil_status_fwconnected, wil->status)) + wil_update_net_queues_bh(wil, NULL, false); return -EBUSY; } diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index 439fe30936b6..8f1e79b425cf 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1930,6 +1930,11 @@ static inline void __wil_update_net_queues(struct wil6210_priv *wil, return; } + /* Do not wake the queues in suspend flow */ + if (test_bit(wil_status_suspending, wil->status) || + test_bit(wil_status_suspended, wil->status)) + return; + /* check wake */ for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { struct vring *cur_vring = &wil->vring_tx[i]; diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index e76ec6ed9995..9525f521d215 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -59,7 +59,8 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1); } -#define WIL6210_MEM_SIZE (2*1024*1024UL) +#define WIL6210_MIN_MEM_SIZE (2 * 1024 * 1024UL) +#define WIL6210_MAX_MEM_SIZE (4 * 1024 * 1024UL) #define WIL_TX_Q_LEN_DEFAULT (4000) #define WIL_RX_RING_SIZE_ORDER_DEFAULT (10) @@ -620,6 +621,7 @@ extern u8 led_polarity; struct wil6210_priv { struct pci_dev *pdev; + u32 bar_size; struct wireless_dev *wdev; void __iomem *csr; DECLARE_BITMAP(status, wil_status_last); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 24878ecf7e93..fba0d6a79ae2 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -160,7 +160,7 @@ void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) return NULL; off = HOSTADDR(ptr); - if (off > WIL6210_MEM_SIZE - 4) + if (off > wil->bar_size - 4) return NULL; return wil->csr + off; @@ -180,7 +180,7 @@ void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr) return NULL; off = HOSTADDR(ptr); - if (off > WIL6210_MEM_SIZE - 4) + if (off > wil->bar_size - 4) return NULL; return wil->csr + off; diff --git a/drivers/net/wireless/cnss/Kconfig b/drivers/net/wireless/cnss/Kconfig index 8e69a2b469b9..8946f65df716 100644 --- a/drivers/net/wireless/cnss/Kconfig +++ b/drivers/net/wireless/cnss/Kconfig @@ -1,5 +1,6 @@ config CNSS tristate "CNSS driver for wifi module" + select CNSS_UTILS select CRYPTO select CRYPTO_HASH select CRYPTO_BLKCIPHER diff --git a/drivers/net/wireless/cnss/cnss_pci.c b/drivers/net/wireless/cnss/cnss_pci.c index 48d358c4722a..22c59d8c3c45 100644 --- a/drivers/net/wireless/cnss/cnss_pci.c +++ b/drivers/net/wireless/cnss/cnss_pci.c @@ -134,6 +134,7 @@ static DEFINE_SPINLOCK(pci_link_down_lock); #define FW_IMAGE_MISSION (0x02) #define FW_IMAGE_BDATA (0x03) #define FW_IMAGE_PRINT (0x04) +#define FW_SETUP_DELAY 2000 #define SEG_METADATA (0x01) #define SEG_NON_PAGED (0x02) @@ -244,7 +245,7 @@ static struct cnss_data { struct pci_saved_state *saved_state; u16 revision_id; bool recovery_in_progress; - bool fw_available; + atomic_t fw_available; struct codeswap_codeseg_info *cnss_seg_info; /* Virtual Address of the DMA page */ void *codeseg_cpuaddr[CODESWAP_MAX_CODESEGS]; @@ -273,6 +274,10 @@ static struct cnss_data { u32 fw_dma_size; u32 fw_seg_count; struct segment_memory fw_seg_mem[MAX_NUM_OF_SEGMENTS]; + atomic_t fw_store_in_progress; + /* Firmware setup complete lock */ + struct mutex fw_setup_stat_lock; + struct completion fw_setup_complete; void *bdata_cpu; dma_addr_t bdata_dma; u32 bdata_dma_size; @@ -1369,10 +1374,21 @@ int cnss_get_fw_image(struct image_desc_info *image_desc_info) !penv->fw_seg_count || !penv->bdata_seg_count) return -EINVAL; + /* Check for firmware setup trigger by usersapce is in progress + * and wait for complition of firmware setup. + */ + + if (atomic_read(&penv->fw_store_in_progress)) { + wait_for_completion_timeout(&penv->fw_setup_complete, + msecs_to_jiffies(FW_SETUP_DELAY)); + } + + mutex_lock(&penv->fw_setup_stat_lock); image_desc_info->fw_addr = penv->fw_dma; image_desc_info->fw_size = penv->fw_dma_size; image_desc_info->bdata_addr = penv->bdata_dma; image_desc_info->bdata_size = penv->bdata_dma_size; + mutex_unlock(&penv->fw_setup_stat_lock); return 0; } @@ -1552,7 +1568,7 @@ static int cnss_wlan_pci_probe(struct pci_dev *pdev, penv->pdev = pdev; penv->id = id; - penv->fw_available = false; + atomic_set(&penv->fw_available, 0); penv->device_id = pdev->device; if (penv->smmu_iova_len) { @@ -1858,8 +1874,19 @@ static ssize_t fw_image_setup_store(struct device *dev, if (!penv) return -ENODEV; - if (sscanf(buf, "%d", &val) != 1) + if (atomic_read(&penv->fw_store_in_progress)) { + pr_info("%s: Firmware setup in progress\n", __func__); + return 0; + } + + atomic_set(&penv->fw_store_in_progress, 1); + init_completion(&penv->fw_setup_complete); + + if (kstrtoint(buf, 0, &val)) { + atomic_set(&penv->fw_store_in_progress, 0); + complete(&penv->fw_setup_complete); return -EINVAL; + } if (val == FW_IMAGE_FTM || val == FW_IMAGE_MISSION || val == FW_IMAGE_BDATA) { @@ -1868,6 +1895,8 @@ static ssize_t fw_image_setup_store(struct device *dev, if (ret != 0) { pr_err("%s: Invalid parsing of FW image files %d", __func__, ret); + atomic_set(&penv->fw_store_in_progress, 0); + complete(&penv->fw_setup_complete); return -EINVAL; } penv->fw_image_setup = val; @@ -1877,6 +1906,9 @@ static ssize_t fw_image_setup_store(struct device *dev, penv->bmi_test = val; } + atomic_set(&penv->fw_store_in_progress, 0); + complete(&penv->fw_setup_complete); + return count; } @@ -1979,7 +2011,7 @@ int cnss_get_codeswap_struct(struct codeswap_codeseg_info *swap_seg) swap_seg = NULL; return -ENOENT; } - if (!penv->fw_available) { + if (!atomic_read(&penv->fw_available)) { pr_debug("%s: fw is not available\n", __func__); return -ENOENT; } @@ -2003,6 +2035,16 @@ static void cnss_wlan_memory_expansion(void) u_int32_t total_length = 0; struct pci_dev *pdev; + /* Check for firmware setup trigger by usersapce is in progress + * and wait for complition of firmware setup. + */ + + if (atomic_read(&penv->fw_store_in_progress)) { + wait_for_completion_timeout(&penv->fw_setup_complete, + msecs_to_jiffies(FW_SETUP_DELAY)); + } + + mutex_lock(&penv->fw_setup_stat_lock); filename = cnss_wlan_get_evicted_data_file(); pdev = penv->pdev; dev = &pdev->dev; @@ -2010,21 +2052,25 @@ static void cnss_wlan_memory_expansion(void) if (!cnss_seg_info) { pr_debug("cnss: cnss_seg_info is NULL\n"); + mutex_unlock(&penv->fw_setup_stat_lock); goto end; } - if (penv->fw_available) { + if (atomic_read(&penv->fw_available)) { pr_debug("cnss: fw code already copied to host memory\n"); + mutex_unlock(&penv->fw_setup_stat_lock); goto end; } if (request_firmware(&fw_entry, filename, dev) != 0) { pr_debug("cnss: failed to get fw: %s\n", filename); + mutex_unlock(&penv->fw_setup_stat_lock); goto end; } if (!fw_entry || !fw_entry->data) { pr_err("%s: INVALID FW entries\n", __func__); + mutex_unlock(&penv->fw_setup_stat_lock); goto release_fw; } @@ -2059,7 +2105,9 @@ static void cnss_wlan_memory_expansion(void) } pr_debug("cnss: total_bytes copied: %d\n", total_length); cnss_seg_info->codeseg_total_bytes = total_length; - penv->fw_available = 1; + + atomic_set(&penv->fw_available, 1); + mutex_unlock(&penv->fw_setup_stat_lock); release_fw: release_firmware(fw_entry); @@ -2968,6 +3016,8 @@ skip_ramdump: memset(phys_to_virt(0), 0, SZ_4K); #endif + atomic_set(&penv->fw_store_in_progress, 0); + mutex_init(&penv->fw_setup_stat_lock); ret = device_create_file(dev, &dev_attr_fw_image_setup); if (ret) { pr_err("cnss: fw_image_setup sys file creation failed\n"); diff --git a/drivers/net/wireless/cnss_utils/Kconfig b/drivers/net/wireless/cnss_utils/Kconfig new file mode 100644 index 000000000000..5f43e4872d65 --- /dev/null +++ b/drivers/net/wireless/cnss_utils/Kconfig @@ -0,0 +1,6 @@ +config CNSS_UTILS + bool "CNSS utilities support" + ---help--- + Add CNSS utilities support for the WLAN driver module. + This feature enable wlan driver to use CNSS utilities APIs to set + and get wlan related information.
\ No newline at end of file diff --git a/drivers/net/wireless/cnss_utils/Makefile b/drivers/net/wireless/cnss_utils/Makefile new file mode 100644 index 000000000000..0d1ed7ae939e --- /dev/null +++ b/drivers/net/wireless/cnss_utils/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_CNSS_UTILS) += cnss_utils.o diff --git a/drivers/net/wireless/cnss_utils/cnss_utils.c b/drivers/net/wireless/cnss_utils/cnss_utils.c new file mode 100644 index 000000000000..a452900868c4 --- /dev/null +++ b/drivers/net/wireless/cnss_utils/cnss_utils.c @@ -0,0 +1,310 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#define pr_fmt(fmt) "cnss_utils: " fmt + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/etherdevice.h> +#include <net/cnss_utils.h> + +#define CNSS_MAX_CH_NUM 45 +struct cnss_unsafe_channel_list { + u16 unsafe_ch_count; + u16 unsafe_ch_list[CNSS_MAX_CH_NUM]; +}; + +struct cnss_dfs_nol_info { + void *dfs_nol_info; + u16 dfs_nol_info_len; +}; + +#define MAX_NO_OF_MAC_ADDR 4 +struct cnss_wlan_mac_addr { + u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN]; + u32 no_of_mac_addr_set; +}; + +static struct cnss_utils_priv { + struct cnss_unsafe_channel_list unsafe_channel_list; + struct cnss_dfs_nol_info dfs_nol_info; + /* generic mutex for unsafe channel */ + struct mutex unsafe_channel_list_lock; + /* generic spin-lock for dfs_nol info */ + spinlock_t dfs_nol_info_lock; + int driver_load_cnt; + bool is_wlan_mac_set; + struct cnss_wlan_mac_addr wlan_mac_addr; + enum cnss_utils_cc_src cc_source; +} *cnss_utils_priv; + +int cnss_utils_set_wlan_unsafe_channel(struct device *dev, + u16 *unsafe_ch_list, u16 ch_count) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + mutex_lock(&priv->unsafe_channel_list_lock); + if ((!unsafe_ch_list) || (ch_count > CNSS_MAX_CH_NUM)) { + mutex_unlock(&priv->unsafe_channel_list_lock); + return -EINVAL; + } + + priv->unsafe_channel_list.unsafe_ch_count = ch_count; + + if (ch_count == 0) + goto end; + + memcpy(priv->unsafe_channel_list.unsafe_ch_list, + unsafe_ch_list, ch_count * sizeof(u16)); + +end: + mutex_unlock(&priv->unsafe_channel_list_lock); + + return 0; +} +EXPORT_SYMBOL(cnss_utils_set_wlan_unsafe_channel); + +int cnss_utils_get_wlan_unsafe_channel(struct device *dev, + u16 *unsafe_ch_list, + u16 *ch_count, u16 buf_len) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + mutex_lock(&priv->unsafe_channel_list_lock); + if (!unsafe_ch_list || !ch_count) { + mutex_unlock(&priv->unsafe_channel_list_lock); + return -EINVAL; + } + + if (buf_len < + (priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16))) { + mutex_unlock(&priv->unsafe_channel_list_lock); + return -ENOMEM; + } + + *ch_count = priv->unsafe_channel_list.unsafe_ch_count; + memcpy(unsafe_ch_list, priv->unsafe_channel_list.unsafe_ch_list, + priv->unsafe_channel_list.unsafe_ch_count * sizeof(u16)); + mutex_unlock(&priv->unsafe_channel_list_lock); + + return 0; +} +EXPORT_SYMBOL(cnss_utils_get_wlan_unsafe_channel); + +int cnss_utils_wlan_set_dfs_nol(struct device *dev, + const void *info, u16 info_len) +{ + void *temp; + void *old_nol_info; + struct cnss_dfs_nol_info *dfs_info; + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + if (!info || !info_len) + return -EINVAL; + + temp = kmalloc(info_len, GFP_ATOMIC); + if (!temp) + return -ENOMEM; + + memcpy(temp, info, info_len); + spin_lock_bh(&priv->dfs_nol_info_lock); + dfs_info = &priv->dfs_nol_info; + old_nol_info = dfs_info->dfs_nol_info; + dfs_info->dfs_nol_info = temp; + dfs_info->dfs_nol_info_len = info_len; + spin_unlock_bh(&priv->dfs_nol_info_lock); + kfree(old_nol_info); + + return 0; +} +EXPORT_SYMBOL(cnss_utils_wlan_set_dfs_nol); + +int cnss_utils_wlan_get_dfs_nol(struct device *dev, + void *info, u16 info_len) +{ + int len; + struct cnss_dfs_nol_info *dfs_info; + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + if (!info || !info_len) + return -EINVAL; + + spin_lock_bh(&priv->dfs_nol_info_lock); + + dfs_info = &priv->dfs_nol_info; + if (!dfs_info->dfs_nol_info || + dfs_info->dfs_nol_info_len == 0) { + spin_unlock_bh(&priv->dfs_nol_info_lock); + return -ENOENT; + } + + len = min(info_len, dfs_info->dfs_nol_info_len); + memcpy(info, dfs_info->dfs_nol_info, len); + spin_unlock_bh(&priv->dfs_nol_info_lock); + + return len; +} +EXPORT_SYMBOL(cnss_utils_wlan_get_dfs_nol); + +void cnss_utils_increment_driver_load_cnt(struct device *dev) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return; + + ++(priv->driver_load_cnt); +} +EXPORT_SYMBOL(cnss_utils_increment_driver_load_cnt); + +int cnss_utils_get_driver_load_cnt(struct device *dev) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + return priv->driver_load_cnt; +} +EXPORT_SYMBOL(cnss_utils_get_driver_load_cnt); + +int cnss_utils_set_wlan_mac_address(const u8 *in, const uint32_t len) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + u32 no_of_mac_addr; + struct cnss_wlan_mac_addr *addr = NULL; + int iter; + u8 *temp = NULL; + + if (!priv) + return -EINVAL; + + if (priv->is_wlan_mac_set) { + pr_debug("WLAN MAC address is already set\n"); + return 0; + } + + if (len == 0 || (len % ETH_ALEN) != 0) { + pr_err("Invalid length %d\n", len); + return -EINVAL; + } + + no_of_mac_addr = len / ETH_ALEN; + if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) { + pr_err("Exceed maximum supported MAC address %u %u\n", + MAX_NO_OF_MAC_ADDR, no_of_mac_addr); + return -EINVAL; + } + + priv->is_wlan_mac_set = true; + addr = &priv->wlan_mac_addr; + addr->no_of_mac_addr_set = no_of_mac_addr; + temp = &addr->mac_addr[0][0]; + + for (iter = 0; iter < no_of_mac_addr; + ++iter, temp += ETH_ALEN, in += ETH_ALEN) { + ether_addr_copy(temp, in); + pr_debug("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n", + temp[0], temp[1], temp[2], + temp[3], temp[4], temp[5]); + } + + return 0; +} +EXPORT_SYMBOL(cnss_utils_set_wlan_mac_address); + +u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + struct cnss_wlan_mac_addr *addr = NULL; + + if (!priv) + goto out; + + if (!priv->is_wlan_mac_set) { + pr_debug("WLAN MAC address is not set\n"); + goto out; + } + + addr = &priv->wlan_mac_addr; + *num = addr->no_of_mac_addr_set; + return &addr->mac_addr[0][0]; +out: + *num = 0; + return NULL; +} +EXPORT_SYMBOL(cnss_utils_get_wlan_mac_address); + +void cnss_utils_set_cc_source(struct device *dev, + enum cnss_utils_cc_src cc_source) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return; + + priv->cc_source = cc_source; +} +EXPORT_SYMBOL(cnss_utils_set_cc_source); + +enum cnss_utils_cc_src cnss_utils_get_cc_source(struct device *dev) +{ + struct cnss_utils_priv *priv = cnss_utils_priv; + + if (!priv) + return -EINVAL; + + return priv->cc_source; +} +EXPORT_SYMBOL(cnss_utils_get_cc_source); + +static int __init cnss_utils_init(void) +{ + struct cnss_utils_priv *priv = NULL; + + priv = kmalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->cc_source = CNSS_UTILS_SOURCE_CORE; + + mutex_init(&priv->unsafe_channel_list_lock); + spin_lock_init(&priv->dfs_nol_info_lock); + + cnss_utils_priv = priv; + + return 0; +} + +static void __exit cnss_utils_exit(void) +{ + kfree(cnss_utils_priv); + cnss_utils_priv = NULL; +} + +module_init(cnss_utils_init); +module_exit(cnss_utils_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION(DEVICE "CNSS Utilities Driver"); diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c index 95a7fdb3cc1c..c602a1e674ca 100644 --- a/drivers/net/wireless/cw1200/sta.c +++ b/drivers/net/wireless/cw1200/sta.c @@ -2135,9 +2135,7 @@ void cw1200_mcast_timeout(unsigned long arg) int cw1200_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { /* Aggregation is implemented fully in firmware, * including block ack negotiation. Do not allow diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 6656215a13a9..04b0349a6ad9 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -5982,12 +5982,14 @@ il4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, int il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 * ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct il_priv *il = hw->priv; int ret = -EINVAL; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; D_HT("A-MPDU action on addr %pM tid %d\n", sta->addr, tid); diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index b3ad34e8bf5a..1eb1a823a111 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -729,12 +729,15 @@ static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg) static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); int ret = -EINVAL; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + u8 buf_size = params->buf_size; struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index ce12717e656a..1a8ea775de08 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -826,13 +826,16 @@ iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif, static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, - u16 *ssn, u8 buf_size, bool amsdu) + struct ieee80211_ampdu_params *params) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; bool tx_agg_ref = false; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 *ssn = ¶ms->ssn; + u8 buf_size = params->buf_size; IWL_DEBUG_HT(mvm, "A-MPDU action on addr %pM tid %d: action %d\n", sta->addr, tid, action); diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 21192b6f9c64..268e50ba88a5 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -947,6 +947,7 @@ static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) if (card && card->cmd_buf) { mwifiex_unmap_pci_memory(adapter, card->cmd_buf, PCI_DMA_TODEVICE); + dev_kfree_skb_any(card->cmd_buf); } return 0; } @@ -1513,6 +1514,11 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) return -1; card->cmd_buf = skb; + /* + * Need to keep a reference, since core driver might free up this + * buffer before we've unmapped it. + */ + skb_get(skb); /* To send a command, the driver will: 1. Write the 64bit physical address of the data buffer to @@ -1610,6 +1616,7 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) if (card->cmd_buf) { mwifiex_unmap_pci_memory(adapter, card->cmd_buf, PCI_DMA_TODEVICE); + dev_kfree_skb_any(card->cmd_buf); card->cmd_buf = NULL; } diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c index 9b4d8a637915..4b354918e183 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c @@ -359,6 +359,107 @@ bool rtl8821ae_phy_rf_config(struct ieee80211_hw *hw) return rtl8821ae_phy_rf6052_config(hw); } +static void _rtl8812ae_phy_set_rfe_reg_24g(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 tmp; + + switch (rtlhal->rfe_type) { + case 3: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x54337770); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x54337770); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, 0x900, 0x00000303, 0x1); + break; + case 4: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77777777); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x001); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x001); + break; + case 5: + rtl_write_byte(rtlpriv, RA_RFE_PINMUX + 2, 0x77); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777); + tmp = rtl_read_byte(rtlpriv, RA_RFE_INV + 3); + rtl_write_byte(rtlpriv, RA_RFE_INV + 3, tmp & ~0x1); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + break; + case 1: + if (rtlpriv->btcoexist.bt_coexistence) { + rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xffffff, 0x777777); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, + 0x77777777); + rtl_set_bbreg(hw, RA_RFE_INV, 0x33f00000, 0x000); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + break; + } + case 0: + case 2: + default: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77777777); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x000); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + break; + } +} + +static void _rtl8812ae_phy_set_rfe_reg_5g(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 tmp; + + switch (rtlhal->rfe_type) { + case 0: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77337717); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337717); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + break; + case 1: + if (rtlpriv->btcoexist.bt_coexistence) { + rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xffffff, 0x337717); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, + 0x77337717); + rtl_set_bbreg(hw, RA_RFE_INV, 0x33f00000, 0x000); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + } else { + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, + 0x77337717); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, + 0x77337717); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x000); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000); + } + break; + case 3: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x54337717); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x54337717); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, 0x900, 0x00000303, 0x1); + break; + case 5: + rtl_write_byte(rtlpriv, RA_RFE_PINMUX + 2, 0x33); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337777); + tmp = rtl_read_byte(rtlpriv, RA_RFE_INV + 3); + rtl_write_byte(rtlpriv, RA_RFE_INV + 3, tmp | 0x1); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + break; + case 2: + case 4: + default: + rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77337777); + rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337777); + rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010); + rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010); + break; + } +} + u32 phy_get_tx_swing_8812A(struct ieee80211_hw *hw, u8 band, u8 rf_path) { @@ -553,14 +654,9 @@ void rtl8821ae_phy_switch_wirelessband(struct ieee80211_hw *hw, u8 band) /* 0x82C[1:0] = 2b'00 */ rtl_set_bbreg(hw, 0x82c, 0x3, 0); } - if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) { - rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, - 0x77777777); - rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, - 0x77777777); - rtl_set_bbreg(hw, RA_RFE_INV, 0x3ff00000, 0x000); - rtl_set_bbreg(hw, RB_RFE_INV, 0x3ff00000, 0x000); - } + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) + _rtl8812ae_phy_set_rfe_reg_24g(hw); rtl_set_bbreg(hw, RTXPATH, 0xf0, 0x1); rtl_set_bbreg(hw, RCCK_RX, 0x0f000000, 0x1); @@ -615,14 +711,8 @@ void rtl8821ae_phy_switch_wirelessband(struct ieee80211_hw *hw, u8 band) /* 0x82C[1:0] = 2'b00 */ rtl_set_bbreg(hw, 0x82c, 0x3, 1); - if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) { - rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, - 0x77337777); - rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, - 0x77337777); - rtl_set_bbreg(hw, RA_RFE_INV, 0x3ff00000, 0x010); - rtl_set_bbreg(hw, RB_RFE_INV, 0x3ff00000, 0x010); - } + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) + _rtl8812ae_phy_set_rfe_reg_5g(hw); rtl_set_bbreg(hw, RTXPATH, 0xf0, 0); rtl_set_bbreg(hw, RCCK_RX, 0x0f000000, 0xf); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h index 1d6110f9c1fb..ed69dbe178ff 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/reg.h @@ -2424,6 +2424,7 @@ #define BMASKH4BITS 0xf0000000 #define BMASKOFDM_D 0xffc00000 #define BMASKCCK 0x3f3f3f3f +#define BMASKRFEINV 0x3ff00000 #define BRFREGOFFSETMASK 0xfffff diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c index 09c7e098f460..085ef5c87262 100644 --- a/drivers/net/wireless/ti/wl18xx/event.c +++ b/drivers/net/wireless/ti/wl18xx/event.c @@ -206,5 +206,33 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl) mbox->sc_pwd_len, mbox->sc_pwd); + if (vector & RX_BA_WIN_SIZE_CHANGE_EVENT_ID) { + struct wl12xx_vif *wlvif; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + u8 link_id = mbox->rx_ba_link_id; + u8 win_size = mbox->rx_ba_win_size; + const u8 *addr; + + wlvif = wl->links[link_id].wlvif; + vif = wl12xx_wlvif_to_vif(wlvif); + + /* Update RX aggregation window size and call + * MAC routine to stop active RX aggregations for this link + */ + if (wlvif->bss_type != BSS_TYPE_AP_BSS) + addr = vif->bss_conf.bssid; + else + addr = wl->links[link_id].addr; + + sta = ieee80211_find_sta(vif, addr); + if (sta) { + sta->max_rx_aggregation_subframes = win_size; + ieee80211_stop_rx_ba_session(vif, + wl->links[link_id].ba_bitmap, + addr); + } + } + return 0; } diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h index f3d4f13379cb..9495fadc8093 100644 --- a/drivers/net/wireless/ti/wl18xx/event.h +++ b/drivers/net/wireless/ti/wl18xx/event.h @@ -38,6 +38,7 @@ enum { REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(18), DFS_CHANNELS_CONFIG_COMPLETE_EVENT = BIT(19), PERIODIC_SCAN_REPORT_EVENT_ID = BIT(20), + RX_BA_WIN_SIZE_CHANGE_EVENT_ID = BIT(21), SMART_CONFIG_SYNC_EVENT_ID = BIT(22), SMART_CONFIG_DECODE_EVENT_ID = BIT(23), TIME_SYNC_EVENT_ID = BIT(24), diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 50cce42089a5..47f355e92193 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1029,7 +1029,8 @@ static int wl18xx_boot(struct wl1271 *wl) DFS_CHANNELS_CONFIG_COMPLETE_EVENT | SMART_CONFIG_SYNC_EVENT_ID | SMART_CONFIG_DECODE_EVENT_ID | - TIME_SYNC_EVENT_ID; + TIME_SYNC_EVENT_ID | + RX_BA_WIN_SIZE_CHANGE_EVENT_ID; wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID; diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index f28fa3b5029d..0646c9b6f8d7 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -1419,7 +1419,8 @@ out: /* setup BA session receiver setting in the FW. */ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, - u16 ssn, bool enable, u8 peer_hlid) + u16 ssn, bool enable, u8 peer_hlid, + u8 win_size) { struct wl1271_acx_ba_receiver_setup *acx; int ret; @@ -1435,7 +1436,7 @@ int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, acx->hlid = peer_hlid; acx->tid = tid_index; acx->enable = enable; - acx->win_size = wl->conf.ht.rx_ba_win_size; + acx->win_size = win_size; acx->ssn = ssn; ret = wlcore_cmd_configure_failsafe(wl, ACX_BA_SESSION_RX_SETUP, acx, diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index 954d57ec98f4..524aea495dff 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -1112,7 +1112,8 @@ int wl1271_acx_set_ht_information(struct wl1271 *wl, int wl12xx_acx_set_ba_initiator_policy(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, - u16 ssn, bool enable, u8 peer_hlid); + u16 ssn, bool enable, u8 peer_hlid, + u8 win_size); int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif, u64 *mactime); int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif, diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 30165ea0fa25..7b27c7e23af2 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5328,7 +5328,9 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, } ret = wl12xx_acx_set_ba_receiver_session(wl, tid, *ssn, true, - hlid); + hlid, + params->buf_size); + if (!ret) { *ba_bitmap |= BIT(tid); wl->ba_rx_session_count++; @@ -5349,7 +5351,7 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, } ret = wl12xx_acx_set_ba_receiver_session(wl, tid, 0, false, - hlid); + hlid, 0); if (!ret) { *ba_bitmap &= ~BIT(tid); wl->ba_rx_session_count--; diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 1f445f357da1..888e9cfef51a 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -304,7 +304,7 @@ static void xennet_alloc_rx_buffers(struct netfront_queue *queue) queue->rx_skbs[id] = skb; ref = gnttab_claim_grant_reference(&queue->gref_rx_head); - BUG_ON((signed short)ref < 0); + WARN_ON_ONCE(IS_ERR_VALUE((unsigned long)(int)ref)); queue->grant_rx_ref[id] = ref; page = skb_frag_page(&skb_shinfo(skb)->frags[0]); @@ -437,7 +437,7 @@ static void xennet_tx_setup_grant(unsigned long gfn, unsigned int offset, id = get_id_from_freelist(&queue->tx_skb_freelist, queue->tx_skbs); tx = RING_GET_REQUEST(&queue->tx, queue->tx.req_prod_pvt++); ref = gnttab_claim_grant_reference(&queue->gref_tx_head); - BUG_ON((signed short)ref < 0); + WARN_ON_ONCE(IS_ERR_VALUE((unsigned long)(int)ref)); gnttab_grant_foreign_access_ref(ref, queue->info->xbdev->otherend_id, gfn, GNTMAP_readonly); diff --git a/drivers/of/address.c b/drivers/of/address.c index ec5eb17ae283..5393be762da4 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -260,7 +260,7 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, if (!parser->range || parser->range + parser->np > parser->end) return NULL; - range->pci_space = parser->range[0]; + range->pci_space = be32_to_cpup(parser->range); range->flags = of_bus_pci_get_flags(parser->range); range->pci_addr = of_read_number(parser->range + 1, ns); range->cpu_addr = of_translate_address(parser->node, diff --git a/drivers/parport/parport_gsc.c b/drivers/parport/parport_gsc.c index 6e3a60c78873..50f3bb0dd1f1 100644 --- a/drivers/parport/parport_gsc.c +++ b/drivers/parport/parport_gsc.c @@ -293,7 +293,7 @@ struct parport *parport_gsc_probe_port(unsigned long base, p->irq = PARPORT_IRQ_NONE; } if (p->irq != PARPORT_IRQ_NONE) { - printk(", irq %d", p->irq); + pr_cont(", irq %d", p->irq); if (p->dma == PARPORT_DMA_AUTO) { p->dma = PARPORT_DMA_NONE; @@ -303,8 +303,8 @@ struct parport *parport_gsc_probe_port(unsigned long base, is mandatory (see above) */ p->dma = PARPORT_DMA_NONE; - printk(" ["); -#define printmode(x) {if(p->modes&PARPORT_MODE_##x){printk("%s%s",f?",":"",#x);f++;}} + pr_cont(" ["); +#define printmode(x) {if(p->modes&PARPORT_MODE_##x){pr_cont("%s%s",f?",":"",#x);f++;}} { int f = 0; printmode(PCSPP); @@ -315,7 +315,7 @@ struct parport *parport_gsc_probe_port(unsigned long base, // printmode(DMA); } #undef printmode - printk("]\n"); + pr_cont("]\n"); if (p->irq != PARPORT_IRQ_NONE) { if (request_irq (p->irq, parport_irq_handler, diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c index 1aebd49220b0..217c7ce3f57b 100644 --- a/drivers/pci/host/pci-msm.c +++ b/drivers/pci/host/pci-msm.c @@ -4675,6 +4675,8 @@ int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options) goto link_fail; } + msleep(500); + msm_pcie_config_controller(dev); if (!dev->msi_gicm_addr) @@ -5608,16 +5610,18 @@ static void msm_pcie_unmap_qgic_addr(struct msm_pcie_dev_t *dev, } } -void msm_pcie_destroy_irq(unsigned int irq) +void msm_pcie_destroy_irq(unsigned int irq, struct pci_dev *pdev) { int pos; - struct pci_dev *pdev = irq_get_chip_data(irq); struct msi_desc *entry = irq_get_msi_desc(irq); struct msi_desc *firstentry; struct msm_pcie_dev_t *dev; u32 nvec; int firstirq; + if (!pdev) + pdev = irq_get_chip_data(irq); + if (!pdev) { pr_err("PCIe: pci device is null. IRQ:%d\n", irq); return; @@ -5677,7 +5681,7 @@ void msm_pcie_destroy_irq(unsigned int irq) void arch_teardown_msi_irq(unsigned int irq) { PCIE_GEN_DBG("irq %d deallocated\n", irq); - msm_pcie_destroy_irq(irq); + msm_pcie_destroy_irq(irq, NULL); } void arch_teardown_msi_irqs(struct pci_dev *dev) @@ -5696,7 +5700,7 @@ void arch_teardown_msi_irqs(struct pci_dev *dev) continue; nvec = 1 << entry->msi_attrib.multiple; for (i = 0; i < nvec; i++) - arch_teardown_msi_irq(entry->irq + i); + msm_pcie_destroy_irq(entry->irq + i, dev); } } @@ -5892,7 +5896,6 @@ static int arch_setup_msi_irq_qgic(struct pci_dev *pdev, firstirq = irq; irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING); - irq_set_chip_data(irq, pdev); } /* write msi vector and data */ diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d7508704c992..f8b2b5987ea9 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -973,15 +973,19 @@ void pci_remove_legacy_files(struct pci_bus *b) int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma, enum pci_mmap_api mmap_api) { - unsigned long nr, start, size, pci_start; + unsigned long nr, start, size; + resource_size_t pci_start = 0, pci_end; if (pci_resource_len(pdev, resno) == 0) return 0; nr = vma_pages(vma); start = vma->vm_pgoff; size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1; - pci_start = (mmap_api == PCI_MMAP_PROCFS) ? - pci_resource_start(pdev, resno) >> PAGE_SHIFT : 0; + if (mmap_api == PCI_MMAP_PROCFS) { + pci_resource_to_user(pdev, resno, &pdev->resource[resno], + &pci_start, &pci_end); + pci_start >>= PAGE_SHIFT; + } if (start >= pci_start && start < pci_start + size && start + nr <= pci_start + size) return 1; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 0e53488f8ec1..1a14ca8965e6 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1732,8 +1732,8 @@ static void pci_pme_list_scan(struct work_struct *work) } } if (!list_empty(&pci_pme_list)) - schedule_delayed_work(&pci_pme_work, - msecs_to_jiffies(PME_TIMEOUT)); + queue_delayed_work(system_freezable_wq, &pci_pme_work, + msecs_to_jiffies(PME_TIMEOUT)); mutex_unlock(&pci_pme_list_mutex); } @@ -1798,8 +1798,9 @@ void pci_pme_active(struct pci_dev *dev, bool enable) mutex_lock(&pci_pme_list_mutex); list_add(&pme_dev->list, &pci_pme_list); if (list_is_singular(&pci_pme_list)) - schedule_delayed_work(&pci_pme_work, - msecs_to_jiffies(PME_TIMEOUT)); + queue_delayed_work(system_freezable_wq, + &pci_pme_work, + msecs_to_jiffies(PME_TIMEOUT)); mutex_unlock(&pci_pme_list_mutex); } else { mutex_lock(&pci_pme_list_mutex); diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 39400dda27c2..63ec68e6ac2a 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -552,7 +552,6 @@ static void armpmu_init(struct arm_pmu *armpmu) .stop = armpmu_stop, .read = armpmu_read, .filter_match = armpmu_filter_match, - .events_across_hotplug = 1, }; } diff --git a/drivers/phy/phy-qcom-ufs-qmp-v3.h b/drivers/phy/phy-qcom-ufs-qmp-v3.h index 757ed7464e44..8cb4b0eeb866 100644 --- a/drivers/phy/phy-qcom-ufs-qmp-v3.h +++ b/drivers/phy/phy-qcom-ufs-qmp-v3.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -259,9 +259,9 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_0_0[] = { UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER, 0x04), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN, 0x04), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x4B), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0xF1), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0x81), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW, 0x80), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6C), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6E), UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A), UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x02), UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03), @@ -320,7 +320,7 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_1_0[] = { UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN_QUARTER, 0x04), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SVS_SO_GAIN, 0x04), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_SO_SATURATION_AND_ENABLE, 0x4B), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0xF1), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_PI_CONTROLS, 0x81), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX0_UCDR_FASTLOCK_COUNT_LOW, 0x80), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX1_LANE_MODE_1, 0x06), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_SIGDET_LVL, 0x24), @@ -336,10 +336,10 @@ static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_3_1_0[] = { UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN_QUARTER, 0x04), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SVS_SO_GAIN, 0x04), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_SO_SATURATION_AND_ENABLE, 0x4B), - UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_PI_CONTROLS, 0xF1), + UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_PI_CONTROLS, 0x81), UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX1_UCDR_FASTLOCK_COUNT_LOW, 0x80), UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_MULTI_LANE_CTRL1, 0x02), - UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6C), + UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL2, 0x6E), UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_LARGE_AMP_DRV_LVL, 0x0A), UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_TX_SMALL_AMP_DRV_LVL, 0x02), UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SYM_RESYNC_CTRL, 0x03), diff --git a/drivers/pinctrl/berlin/berlin-bg4ct.c b/drivers/pinctrl/berlin/berlin-bg4ct.c index 09172043d589..c617ec49e9ed 100644 --- a/drivers/pinctrl/berlin/berlin-bg4ct.c +++ b/drivers/pinctrl/berlin/berlin-bg4ct.c @@ -217,7 +217,7 @@ static const struct berlin_desc_group berlin4ct_soc_pinctrl_groups[] = { BERLIN_PINCTRL_GROUP("SCRD0_CRD_PRES", 0xc, 0x3, 0x15, BERLIN_PINCTRL_FUNCTION(0x0, "gpio"), /* GPIO20 */ BERLIN_PINCTRL_FUNCTION(0x1, "scrd0"), /* crd pres */ - BERLIN_PINCTRL_FUNCTION(0x1, "sd1a")), /* DAT3 */ + BERLIN_PINCTRL_FUNCTION(0x3, "sd1a")), /* DAT3 */ BERLIN_PINCTRL_GROUP("SPI1_SS0n", 0xc, 0x3, 0x18, BERLIN_PINCTRL_FUNCTION(0x0, "spi1"), /* SS0n */ BERLIN_PINCTRL_FUNCTION(0x1, "gpio"), /* GPIO37 */ diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c index 08bffc344429..b111a5904952 100644 --- a/drivers/platform/msm/gsi/gsi.c +++ b/drivers/platform/msm/gsi/gsi.c @@ -1319,6 +1319,35 @@ int gsi_query_evt_ring_db_addr(unsigned long evt_ring_hdl, } EXPORT_SYMBOL(gsi_query_evt_ring_db_addr); +int gsi_ring_evt_ring_db(unsigned long evt_ring_hdl, uint64_t value) +{ + struct gsi_evt_ctx *ctx; + + if (!gsi_ctx) { + pr_err("%s:%d gsi context not allocated\n", __func__, __LINE__); + return -GSI_STATUS_NODEV; + } + + if (evt_ring_hdl >= gsi_ctx->max_ev) { + GSIERR("bad params evt_ring_hdl=%lu\n", evt_ring_hdl); + return -GSI_STATUS_INVALID_PARAMS; + } + + ctx = &gsi_ctx->evtr[evt_ring_hdl]; + + if (ctx->state != GSI_EVT_RING_STATE_ALLOCATED) { + GSIERR("bad state %d\n", + gsi_ctx->evtr[evt_ring_hdl].state); + return -GSI_STATUS_UNSUPPORTED_OP; + } + + ctx->ring.wp_local = value; + gsi_ring_evt_doorbell(ctx); + + return GSI_STATUS_SUCCESS; +} +EXPORT_SYMBOL(gsi_ring_evt_ring_db); + int gsi_reset_evt_ring(unsigned long evt_ring_hdl) { uint32_t val; diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c index a02247d3e938..9b3b53dcba68 100644 --- a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c +++ b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, 2016 The Linux Foundation. All rights reserved. +/* Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1059,6 +1059,7 @@ static void ipa_mhi_gsi_ev_err_cb(struct gsi_evt_err_notify *notify) IPA_MHI_ERR("Unexpected err evt: %d\n", notify->evt_id); } IPA_MHI_ERR("err_desc=0x%x\n", notify->err_desc); + ipa_assert(); } static void ipa_mhi_gsi_ch_err_cb(struct gsi_chan_err_notify *notify) @@ -1090,6 +1091,7 @@ static void ipa_mhi_gsi_ch_err_cb(struct gsi_chan_err_notify *notify) IPA_MHI_ERR("Unexpected err evt: %d\n", notify->evt_id); } IPA_MHI_ERR("err_desc=0x%x\n", notify->err_desc); + ipa_assert(); } @@ -2044,6 +2046,8 @@ static int ipa_mhi_suspend_dl(bool force) if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI) ipa_mhi_update_host_ch_state(true); + return 0; + fail_stop_event_update_dl_channel: ipa_mhi_resume_channels(true, ipa_mhi_client_ctx->dl_channels); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c index 40c1971dfe96..10a49b1e75d8 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c @@ -600,12 +600,12 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, mem_size = (ipa_ctx->hdr_proc_ctx_tbl_lcl) ? IPA_MEM_PART(apps_hdr_proc_ctx_size) : IPA_MEM_PART(apps_hdr_proc_ctx_size_ddr); - if (htbl->end + ipa_hdr_proc_ctx_bin_sz[bin] > mem_size) { - IPAERR("hdr proc ctx table overflow\n"); - goto bad_len; - } - if (list_empty(&htbl->head_free_offset_list[bin])) { + if (htbl->end + ipa_hdr_proc_ctx_bin_sz[bin] > mem_size) { + IPAERR("hdr proc ctx table overflow\n"); + goto bad_len; + } + offset = kmem_cache_zalloc(ipa_ctx->hdr_proc_ctx_offset_cache, GFP_KERNEL); if (!offset) { @@ -711,30 +711,30 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr) mem_size = (ipa_ctx->hdr_tbl_lcl) ? IPA_MEM_PART(apps_hdr_size) : IPA_MEM_PART(apps_hdr_size_ddr); - /* - * if header does not fit to table, place it in DDR - * This is valid for IPA 2.5 and on, - * with the exception of IPA2.6L. - */ - if (htbl->end + ipa_hdr_bin_sz[bin] > mem_size) { - if (ipa_ctx->ipa_hw_type != IPA_HW_v2_5) { - IPAERR("not enough room for header\n"); - goto bad_hdr_len; - } else { - entry->is_hdr_proc_ctx = true; - entry->phys_base = dma_map_single(ipa_ctx->pdev, - entry->hdr, - entry->hdr_len, - DMA_TO_DEVICE); - if (dma_mapping_error(ipa_ctx->pdev, - entry->phys_base)) { - IPAERR("dma_map_single failure for entry\n"); - goto fail_dma_mapping; + if (list_empty(&htbl->head_free_offset_list[bin])) { + /* + * if header does not fit to table, place it in DDR + * This is valid for IPA 2.5 and on, + * with the exception of IPA2.6L. + */ + if (htbl->end + ipa_hdr_bin_sz[bin] > mem_size) { + if (ipa_ctx->ipa_hw_type != IPA_HW_v2_5) { + IPAERR("not enough room for header\n"); + goto bad_hdr_len; + } else { + entry->is_hdr_proc_ctx = true; + entry->phys_base = dma_map_single(ipa_ctx->pdev, + entry->hdr, + entry->hdr_len, + DMA_TO_DEVICE); + if (dma_mapping_error(ipa_ctx->pdev, + entry->phys_base)) { + IPAERR("dma_map_single failureed\n"); + goto fail_dma_mapping; + } } - } - } else { - entry->is_hdr_proc_ctx = false; - if (list_empty(&htbl->head_free_offset_list[bin])) { + } else { + entry->is_hdr_proc_ctx = false; offset = kmem_cache_zalloc(ipa_ctx->hdr_offset_cache, GFP_KERNEL); if (!offset) { @@ -751,14 +751,15 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr) htbl->end += ipa_hdr_bin_sz[bin]; list_add(&offset->link, &htbl->head_offset_list[bin]); - } else { - /* get the first free slot */ - offset = - list_first_entry(&htbl->head_free_offset_list[bin], - struct ipa_hdr_offset_entry, link); - list_move(&offset->link, &htbl->head_offset_list[bin]); + entry->offset_entry = offset; } - + } else { + entry->is_hdr_proc_ctx = false; + /* get the first free slot */ + offset = + list_first_entry(&htbl->head_free_offset_list[bin], + struct ipa_hdr_offset_entry, link); + list_move(&offset->link, &htbl->head_offset_list[bin]); entry->offset_entry = offset; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index 2214dfe89df3..f2909110d09f 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c @@ -857,12 +857,16 @@ int ipa2_query_rt_index(struct ipa_ioc_get_rt_tbl_indx *in) return -EINVAL; } + mutex_lock(&ipa_ctx->lock); /* check if this table exists */ entry = __ipa_find_rt_tbl(in->ip, in->name); - if (!entry) + if (!entry) { + mutex_unlock(&ipa_ctx->lock); return -EFAULT; + } in->idx = entry->idx; + mutex_unlock(&ipa_ctx->lock); return 0; } diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c index 364cd4b7d38a..69c88bd04b1b 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc.c @@ -665,6 +665,12 @@ send_cmd: retries++; if (retries == IPA_BAM_STOP_MAX_RETRY) { IPAERR("Failed after %d tries\n", retries); + mutex_unlock(&ipa_ctx->uc_ctx.uc_lock); + /* + * Max retry reached, + * assert to check why cmd send failed. + */ + ipa_assert(); } else { /* sleep for short period to flush IPA */ usleep_range(IPA_UC_WAIT_MIN_SLEEP, diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c index 9943095abe30..e0200fe50871 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c @@ -1008,6 +1008,11 @@ enum ipacm_client_enum ipa2_get_client(int pipe_idx) */ bool ipa2_get_client_uplink(int pipe_idx) { + if (pipe_idx < 0 || pipe_idx >= IPA_MAX_NUM_PIPES) { + IPAERR("invalid pipe idx %d\n", pipe_idx); + return false; + } + return ipa_ctx->ipacm_client[pipe_idx].uplink; } diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index ce899ef9c531..834712a71ac6 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -2618,6 +2618,9 @@ static int rmnet_ipa_set_data_quota_modem(struct wan_ioctl_set_data_quota *data) if (!data->set_quota) ipa_qmi_stop_data_qouta(); + /* prevent string buffer overflows */ + data->interface_name[IFNAMSIZ-1] = '\0'; + index = find_vchannel_name_index(data->interface_name); IPAWANERR("iface name %s, quota %lu\n", data->interface_name, @@ -2855,6 +2858,10 @@ int rmnet_ipa_query_tethering_stats_modem( kfree(req); kfree(resp); return rc; + } else if (data == NULL) { + kfree(req); + kfree(resp); + return 0; } if (resp->dl_dst_pipe_stats_list_valid) { @@ -3038,8 +3045,11 @@ int rmnet_ipa_query_tethering_stats_all( int rmnet_ipa_reset_tethering_stats(struct wan_ioctl_reset_tether_stats *data) { enum ipa_upstream_type upstream_type; + struct wan_ioctl_query_tether_stats tether_stats; int rc = 0; + memset(&tether_stats, 0, sizeof(struct wan_ioctl_query_tether_stats)); + /* get IPA backhaul type */ upstream_type = find_upstream_type(data->upstreamIface); @@ -3057,7 +3067,7 @@ int rmnet_ipa_reset_tethering_stats(struct wan_ioctl_reset_tether_stats *data) } else { IPAWANDBG(" reset modem-backhaul stats\n"); rc = rmnet_ipa_query_tethering_stats_modem( - NULL, true); + &tether_stats, true); if (rc) { IPAWANERR("reset MODEM stats failed\n"); return rc; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c index e349ade46075..bc6622d4725b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c @@ -1668,8 +1668,21 @@ static int ipa3_stop_ul_chan_with_data_drain(u32 qmi_req_id, if (should_force_clear) { result = ipa3_enable_force_clear(qmi_req_id, false, source_pipe_bitmask); - if (result) - goto exit; + if (result) { + struct ipahal_ep_cfg_ctrl_scnd ep_ctrl_scnd = { 0 }; + + /* + * assuming here modem SSR\shutdown, AP can remove + * the delay in this case + */ + IPAERR( + "failed to force clear %d, remove delay from SCND reg\n" + , result); + ep_ctrl_scnd.endp_delay = false; + ipahal_write_reg_n_fields( + IPA_ENDP_INIT_CTRL_SCND_n, clnt_hdl, + &ep_ctrl_scnd); + } } /* with force clear, wait for emptiness */ for (i = 0; i < IPA_POLL_FOR_EMPTINESS_NUM; i++) { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 0492fa27c5b7..6a3a89e2cf8d 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -356,7 +356,7 @@ int ipa3_send_one(struct ipa3_sys_context *sys, struct ipa3_desc *desc, dma_address = desc->dma_address; tx_pkt->no_unmap_dma = true; } - if (!dma_address) { + if (dma_mapping_error(ipa3_ctx->pdev, dma_address)) { IPAERR("failed to DMA wrap\n"); goto fail_dma_map; } @@ -471,7 +471,6 @@ int ipa3_send(struct ipa3_sys_context *sys, int i = 0; int j; int result; - int fail_dma_wrap = 0; uint size; u32 mem_flag = GFP_ATOMIC; int ipa_ep_idx; @@ -527,7 +526,7 @@ int ipa3_send(struct ipa3_sys_context *sys, } dma_addr = dma_map_single(ipa3_ctx->pdev, transfer.iovec, size, DMA_TO_DEVICE); - if (!dma_addr) { + if (dma_mapping_error(ipa3_ctx->pdev, dma_addr)) { IPAERR("dma_map_single failed\n"); kfree(transfer.iovec); return -EFAULT; @@ -540,7 +539,6 @@ int ipa3_send(struct ipa3_sys_context *sys, spin_lock_bh(&sys->spinlock); for (i = 0; i < num_desc; i++) { - fail_dma_wrap = 0; tx_pkt = kmem_cache_zalloc(ipa3_ctx->tx_pkt_wrapper_cache, mem_flag); if (!tx_pkt) { @@ -563,7 +561,7 @@ int ipa3_send(struct ipa3_sys_context *sys, if (ipa_populate_tag_field(&desc[i], tx_pkt, &tag_pyld_ret)) { IPAERR("Failed to populate tag field\n"); - goto failure; + goto failure_dma_map; } } @@ -579,11 +577,6 @@ int ipa3_send(struct ipa3_sys_context *sys, tx_pkt->mem.base, tx_pkt->mem.size, DMA_TO_DEVICE); - if (!tx_pkt->mem.phys_base) { - IPAERR("failed to do dma map.\n"); - fail_dma_wrap = 1; - goto failure; - } } else { tx_pkt->mem.phys_base = desc[i].dma_address; @@ -599,17 +592,17 @@ int ipa3_send(struct ipa3_sys_context *sys, desc[i].frag, 0, tx_pkt->mem.size, DMA_TO_DEVICE); - if (!tx_pkt->mem.phys_base) { - IPAERR("dma map failed\n"); - fail_dma_wrap = 1; - goto failure; - } } else { tx_pkt->mem.phys_base = desc[i].dma_address; tx_pkt->no_unmap_dma = true; } } + if (dma_mapping_error(ipa3_ctx->pdev, tx_pkt->mem.phys_base)) { + IPAERR("failed to do dma map.\n"); + goto failure_dma_map; + } + tx_pkt->sys = sys; tx_pkt->callback = desc[i].callback; tx_pkt->user1 = desc[i].user1; @@ -704,29 +697,31 @@ int ipa3_send(struct ipa3_sys_context *sys, spin_unlock_bh(&sys->spinlock); return 0; +failure_dma_map: + kmem_cache_free(ipa3_ctx->tx_pkt_wrapper_cache, tx_pkt); + failure: ipahal_destroy_imm_cmd(tag_pyld_ret); tx_pkt = tx_pkt_first; for (j = 0; j < i; j++) { next_pkt = list_next_entry(tx_pkt, link); list_del(&tx_pkt->link); - if (desc[j].type != IPA_DATA_DESC_SKB_PAGED) { - dma_unmap_single(ipa3_ctx->pdev, tx_pkt->mem.phys_base, - tx_pkt->mem.size, - DMA_TO_DEVICE); - } else { - dma_unmap_page(ipa3_ctx->pdev, tx_pkt->mem.phys_base, - tx_pkt->mem.size, - DMA_TO_DEVICE); + + if (!tx_pkt->no_unmap_dma) { + if (desc[j].type != IPA_DATA_DESC_SKB_PAGED) { + dma_unmap_single(ipa3_ctx->pdev, + tx_pkt->mem.phys_base, + tx_pkt->mem.size, DMA_TO_DEVICE); + } else { + dma_unmap_page(ipa3_ctx->pdev, + tx_pkt->mem.phys_base, + tx_pkt->mem.size, + DMA_TO_DEVICE); + } } kmem_cache_free(ipa3_ctx->tx_pkt_wrapper_cache, tx_pkt); tx_pkt = next_pkt; } - if (j < num_desc) - /* last desc failed */ - if (fail_dma_wrap) - kmem_cache_free(ipa3_ctx->tx_pkt_wrapper_cache, tx_pkt); - if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) { kfree(gsi_xfer_elem_array); } else { @@ -1973,8 +1968,7 @@ begin: rx_pkt->data.dma_addr = dma_map_single(ipa3_ctx->pdev, ptr, sys->rx_buff_sz, DMA_FROM_DEVICE); - if (rx_pkt->data.dma_addr == 0 || - rx_pkt->data.dma_addr == ~0) { + if (dma_mapping_error(ipa3_ctx->pdev, rx_pkt->data.dma_addr)) { pr_err_ratelimited("%s dma map fail %p for %p sys=%p\n", __func__, (void *)rx_pkt->data.dma_addr, ptr, sys); @@ -2141,8 +2135,7 @@ static void ipa3_alloc_wlan_rx_common_cache(u32 size) ptr = skb_put(rx_pkt->data.skb, IPA_WLAN_RX_BUFF_SZ); rx_pkt->data.dma_addr = dma_map_single(ipa3_ctx->pdev, ptr, IPA_WLAN_RX_BUFF_SZ, DMA_FROM_DEVICE); - if (rx_pkt->data.dma_addr == 0 || - rx_pkt->data.dma_addr == ~0) { + if (dma_mapping_error(ipa3_ctx->pdev, rx_pkt->data.dma_addr)) { IPAERR("dma_map_single failure %p for %p\n", (void *)rx_pkt->data.dma_addr, ptr); goto fail_dma_mapping; @@ -2213,8 +2206,7 @@ static void ipa3_replenish_rx_cache(struct ipa3_sys_context *sys) rx_pkt->data.dma_addr = dma_map_single(ipa3_ctx->pdev, ptr, sys->rx_buff_sz, DMA_FROM_DEVICE); - if (rx_pkt->data.dma_addr == 0 || - rx_pkt->data.dma_addr == ~0) { + if (dma_mapping_error(ipa3_ctx->pdev, rx_pkt->data.dma_addr)) { IPAERR("dma_map_single failure %p for %p\n", (void *)rx_pkt->data.dma_addr, ptr); goto fail_dma_mapping; @@ -2304,8 +2296,8 @@ static void ipa3_replenish_rx_cache_recycle(struct ipa3_sys_context *sys) ptr = skb_put(rx_pkt->data.skb, sys->rx_buff_sz); rx_pkt->data.dma_addr = dma_map_single(ipa3_ctx->pdev, ptr, sys->rx_buff_sz, DMA_FROM_DEVICE); - if (rx_pkt->data.dma_addr == 0 || - rx_pkt->data.dma_addr == ~0) { + if (dma_mapping_error(ipa3_ctx->pdev, + rx_pkt->data.dma_addr)) { IPAERR("dma_map_single failure %p for %p\n", (void *)rx_pkt->data.dma_addr, ptr); goto fail_dma_mapping; @@ -2320,8 +2312,8 @@ static void ipa3_replenish_rx_cache_recycle(struct ipa3_sys_context *sys) ptr = skb_put(rx_pkt->data.skb, sys->rx_buff_sz); rx_pkt->data.dma_addr = dma_map_single(ipa3_ctx->pdev, ptr, sys->rx_buff_sz, DMA_FROM_DEVICE); - if (rx_pkt->data.dma_addr == 0 || - rx_pkt->data.dma_addr == ~0) { + if (dma_mapping_error(ipa3_ctx->pdev, + rx_pkt->data.dma_addr)) { IPAERR("dma_map_single failure %p for %p\n", (void *)rx_pkt->data.dma_addr, ptr); goto fail_dma_mapping; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c index 69dca7666346..a5186f1aff35 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c @@ -377,12 +377,12 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, mem_size = (ipa3_ctx->hdr_proc_ctx_tbl_lcl) ? IPA_MEM_PART(apps_hdr_proc_ctx_size) : IPA_MEM_PART(apps_hdr_proc_ctx_size_ddr); - if (htbl->end + ipa_hdr_proc_ctx_bin_sz[bin] > mem_size) { - IPAERR("hdr proc ctx table overflow\n"); - goto bad_len; - } - if (list_empty(&htbl->head_free_offset_list[bin])) { + if (htbl->end + ipa_hdr_proc_ctx_bin_sz[bin] > mem_size) { + IPAERR("hdr proc ctx table overflow\n"); + goto bad_len; + } + offset = kmem_cache_zalloc(ipa3_ctx->hdr_proc_ctx_offset_cache, GFP_KERNEL); if (!offset) { @@ -487,16 +487,21 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr) mem_size = (ipa3_ctx->hdr_tbl_lcl) ? IPA_MEM_PART(apps_hdr_size) : IPA_MEM_PART(apps_hdr_size_ddr); - /* if header does not fit to table, place it in DDR */ - if (htbl->end + ipa_hdr_bin_sz[bin] > mem_size) { - entry->is_hdr_proc_ctx = true; - entry->phys_base = dma_map_single(ipa3_ctx->pdev, - entry->hdr, - entry->hdr_len, - DMA_TO_DEVICE); - } else { - entry->is_hdr_proc_ctx = false; - if (list_empty(&htbl->head_free_offset_list[bin])) { + if (list_empty(&htbl->head_free_offset_list[bin])) { + /* if header does not fit to table, place it in DDR */ + if (htbl->end + ipa_hdr_bin_sz[bin] > mem_size) { + entry->is_hdr_proc_ctx = true; + entry->phys_base = dma_map_single(ipa3_ctx->pdev, + entry->hdr, + entry->hdr_len, + DMA_TO_DEVICE); + if (dma_mapping_error(ipa3_ctx->pdev, + entry->phys_base)) { + IPAERR("dma_map_single failure for entry\n"); + goto fail_dma_mapping; + } + } else { + entry->is_hdr_proc_ctx = false; offset = kmem_cache_zalloc(ipa3_ctx->hdr_offset_cache, GFP_KERNEL); if (!offset) { @@ -513,14 +518,14 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr) htbl->end += ipa_hdr_bin_sz[bin]; list_add(&offset->link, &htbl->head_offset_list[bin]); - } else { - /* get the first free slot */ - offset = - list_first_entry(&htbl->head_free_offset_list[bin], - struct ipa_hdr_offset_entry, link); - list_move(&offset->link, &htbl->head_offset_list[bin]); + entry->offset_entry = offset; } - + } else { + entry->is_hdr_proc_ctx = false; + /* get the first free slot */ + offset = list_first_entry(&htbl->head_free_offset_list[bin], + struct ipa_hdr_offset_entry, link); + list_move(&offset->link, &htbl->head_offset_list[bin]); entry->offset_entry = offset; } @@ -569,6 +574,9 @@ fail_add_proc_ctx: list_del(&entry->link); dma_unmap_single(ipa3_ctx->pdev, entry->phys_base, entry->hdr_len, DMA_TO_DEVICE); +fail_dma_mapping: + entry->is_hdr_proc_ctx = false; + bad_hdr_len: entry->cookie = 0; kmem_cache_free(ipa3_ctx->hdr_cache, entry); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c index 9e2ffe70170c..cd027c28e597 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c @@ -255,6 +255,24 @@ static int ipa_mhi_start_gsi_channel(enum ipa_client_type client, ep->gsi_evt_ring_hdl = *params->cached_gsi_evt_ring_hdl; } + if (params->ev_ctx_host->wp == params->ev_ctx_host->rbase) { + IPA_MHI_ERR("event ring wp is not updated. base=wp=0x%llx\n", + params->ev_ctx_host->wp); + goto fail_alloc_ch; + return res; + } + + IPA_MHI_DBG("Ring event db: evt_ring_hdl=%lu host_wp=0x%llx\n", + ep->gsi_evt_ring_hdl, params->ev_ctx_host->wp); + res = gsi_ring_evt_ring_db(ep->gsi_evt_ring_hdl, + params->ev_ctx_host->wp); + if (res) { + IPA_MHI_ERR("fail to ring evt ring db %d. hdl=%lu wp=0x%llx\n", + res, ep->gsi_evt_ring_hdl, params->ev_ctx_host->wp); + goto fail_alloc_ch; + return res; + } + memset(&ch_props, 0, sizeof(ch_props)); ch_props.prot = GSI_CHAN_PROT_MHI; ch_props.dir = IPA_CLIENT_IS_PROD(client) ? diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index 7212ba2a165c..6197c9f64ca5 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c @@ -727,12 +727,15 @@ int ipa3_query_rt_index(struct ipa_ioc_get_rt_tbl_indx *in) return -EINVAL; } + mutex_lock(&ipa3_ctx->lock); /* check if this table exists */ entry = __ipa3_find_rt_tbl(in->ip, in->name); - if (!entry) + if (!entry) { + mutex_unlock(&ipa3_ctx->lock); return -EFAULT; - + } in->idx = entry->idx; + mutex_unlock(&ipa3_ctx->lock); return 0; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index d19de2a7bdb5..6647f919a577 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -1064,6 +1064,11 @@ enum ipacm_client_enum ipa3_get_client(int pipe_idx) */ bool ipa3_get_client_uplink(int pipe_idx) { + if (pipe_idx < 0 || pipe_idx >= IPA3_MAX_NUM_PIPES) { + IPAERR("invalid pipe idx %d\n", pipe_idx); + return false; + } + return ipa3_ctx->ipacm_client[pipe_idx].uplink; } diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index 03dbcbb059aa..039bc7da5153 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -1140,7 +1140,8 @@ send: memset(&meta, 0, sizeof(meta)); meta.pkt_init_dst_ep_valid = true; meta.pkt_init_dst_ep_remote = true; - meta.pkt_init_dst_ep = IPA_CLIENT_Q6_LAN_CONS; + meta.pkt_init_dst_ep = + ipa3_get_ep_mapping(IPA_CLIENT_Q6_WAN_CONS); ret = ipa3_tx_dp(IPA_CLIENT_APPS_WAN_PROD, skb, &meta); } else { ret = ipa3_tx_dp(IPA_CLIENT_APPS_WAN_PROD, skb, NULL); @@ -2745,6 +2746,9 @@ static int rmnet_ipa3_set_data_quota_modem( if (!data->set_quota) ipa3_qmi_stop_data_qouta(); + /* prevent string buffer overflows */ + data->interface_name[IFNAMSIZ-1] = '\0'; + index = find_vchannel_name_index(data->interface_name); IPAWANERR("iface name %s, quota %lu\n", data->interface_name, @@ -2982,6 +2986,10 @@ static int rmnet_ipa3_query_tethering_stats_modem( kfree(req); kfree(resp); return rc; + } else if (data == NULL) { + kfree(req); + kfree(resp); + return 0; } if (resp->dl_dst_pipe_stats_list_valid) { @@ -3165,8 +3173,11 @@ int rmnet_ipa3_query_tethering_stats_all( int rmnet_ipa3_reset_tethering_stats(struct wan_ioctl_reset_tether_stats *data) { enum ipa_upstream_type upstream_type; + struct wan_ioctl_query_tether_stats tether_stats; int rc = 0; + memset(&tether_stats, 0, sizeof(struct wan_ioctl_query_tether_stats)); + /* get IPA backhaul type */ upstream_type = find_upstream_type(data->upstreamIface); @@ -3184,7 +3195,7 @@ int rmnet_ipa3_reset_tethering_stats(struct wan_ioctl_reset_tether_stats *data) } else { IPAWANERR(" reset modem-backhaul stats\n"); rc = rmnet_ipa3_query_tethering_stats_modem( - NULL, true); + &tether_stats, true); if (rc) { IPAWANERR("reset MODEM stats failed\n"); return rc; diff --git a/drivers/platform/msm/ipa/test/ipa_test_mhi.c b/drivers/platform/msm/ipa/test/ipa_test_mhi.c index 5a41d641de4f..3a89c7dffdb0 100644 --- a/drivers/platform/msm/ipa/test/ipa_test_mhi.c +++ b/drivers/platform/msm/ipa/test/ipa_test_mhi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -326,6 +326,7 @@ struct ipa_test_mhi_context { struct ipa_mem_buffer out_buffer; u32 prod_hdl; u32 cons_hdl; + u32 test_prod_hdl; }; static struct ipa_test_mhi_context *test_mhi_ctx; @@ -774,6 +775,7 @@ fail_destroy_out_ch_ctx: static int ipa_test_mhi_suite_setup(void **ppriv) { int rc = 0; + struct ipa_sys_connect_params sys_in; IPA_UT_DBG("Start Setup\n"); @@ -815,9 +817,22 @@ static int ipa_test_mhi_suite_setup(void **ppriv) goto fail_free_mmio_spc; } + /* connect PROD pipe for remote wakeup */ + memset(&sys_in, 0, sizeof(struct ipa_sys_connect_params)); + sys_in.client = IPA_CLIENT_TEST_PROD; + sys_in.desc_fifo_sz = IPA_SYS_DESC_FIFO_SZ; + sys_in.ipa_ep_cfg.mode.mode = IPA_DMA; + sys_in.ipa_ep_cfg.mode.dst = IPA_CLIENT_MHI_CONS; + if (ipa_setup_sys_pipe(&sys_in, &test_mhi_ctx->test_prod_hdl)) { + IPA_UT_ERR("setup sys pipe failed.\n"); + goto fail_destroy_data_structures; + } + *ppriv = test_mhi_ctx; return 0; +fail_destroy_data_structures: + ipa_mhi_test_destroy_data_structures(); fail_free_mmio_spc: ipa_test_mhi_free_mmio_space(); fail_iounmap: @@ -838,6 +853,7 @@ static int ipa_test_mhi_suite_teardown(void *priv) if (!test_mhi_ctx) return 0; + ipa_teardown_sys_pipe(test_mhi_ctx->test_prod_hdl); ipa_mhi_test_destroy_data_structures(); ipa_test_mhi_free_mmio_space(); iounmap(test_mhi_ctx->gsi_mmio); @@ -1811,7 +1827,7 @@ static int ipa_mhi_test_create_aggr_open_frame(void) memset(test_mhi_ctx->out_buffer.base + i, i & 0xFF, 1); } - rc = ipa_tx_dp(IPA_CLIENT_MHI_CONS, skb, NULL); + rc = ipa_tx_dp(IPA_CLIENT_TEST_PROD, skb, NULL); if (rc) { IPA_UT_LOG("ipa_tx_dp failed %d\n", rc); IPA_UT_TEST_FAIL_REPORT("ipa tx dp fail"); @@ -1982,7 +1998,7 @@ static int ipa_mhi_test_suspend_host_wakeup(void) memset(test_mhi_ctx->out_buffer.base + i, i & 0xFF, 1); } - rc = ipa_tx_dp(IPA_CLIENT_MHI_CONS, skb, NULL); + rc = ipa_tx_dp(IPA_CLIENT_TEST_PROD, skb, NULL); if (rc) { IPA_UT_LOG("ipa_tx_dp failed %d\n", rc); IPA_UT_TEST_FAIL_REPORT("ipa tx dp fail"); diff --git a/drivers/platform/msm/mhi/mhi_bhi.c b/drivers/platform/msm/mhi/mhi_bhi.c index e1c50e1273ac..68ef2595f3c3 100644 --- a/drivers/platform/msm/mhi/mhi_bhi.c +++ b/drivers/platform/msm/mhi/mhi_bhi.c @@ -249,6 +249,13 @@ int bhi_rddm(struct mhi_device_ctxt *mhi_dev_ctxt, bool in_panic) { struct bhi_ctxt_t *bhi_ctxt = &mhi_dev_ctxt->bhi_ctxt; struct bhie_vec_table *rddm_table = &bhi_ctxt->rddm_table; + struct bhie_mem_info *bhie_mem_info; + u32 rx_sequence, val, current_seq; + u32 timeout = (bhi_ctxt->poll_timeout * 1000) / BHIE_RDDM_DELAY_TIME_US; + int i; + u32 cur_exec, prev_exec = 0; + u32 state, prev_state = 0; + u32 rx_status, prev_status = 0; if (!rddm_table->bhie_mem_info) { mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "RDDM table == NULL\n"); @@ -258,9 +265,93 @@ int bhi_rddm(struct mhi_device_ctxt *mhi_dev_ctxt, bool in_panic) if (!in_panic) return bhi_rddm_graceful(mhi_dev_ctxt); - mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, - "RDDM collection in panic not yet supported\n"); - return -EINVAL; + /* + * Below code should only be executed during kernel panic, + * we expect other cores to be shutting down while we're + * executing rddm transfer. After returning from this function, + * we expect device to reset. + */ + + /* Trigger device into RDDM */ + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "pm_state:0x%x mhi_state:%s\n", + mhi_dev_ctxt->mhi_pm_state, + TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); + if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Register access not allowed\n"); + return -EIO; + } + + /* + * Normally we only set mhi_pm_state after grabbing pm_xfer_lock as a + * write, by function mhi_tryset_pm_state. Since we're in a kernel + * panic, we will set pm state w/o grabbing xfer lock. We're setting + * pm_state to LD as a safety precautions. If another core in middle + * of register access this should deter it. However, there is no + * no gurantee change will take effect. + */ + mhi_dev_ctxt->mhi_pm_state = MHI_PM_LD_ERR_FATAL_DETECT; + /* change should take effect immediately */ + smp_wmb(); + + bhie_mem_info = &rddm_table-> + bhie_mem_info[rddm_table->segment_count - 1]; + rx_sequence = rddm_table->sequence++; + + /* program the vector table */ + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Programming RXVEC table\n"); + val = HIGH_WORD(bhie_mem_info->phys_addr); + mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, + BHIE_RXVECADDR_HIGH_OFFS, val); + val = LOW_WORD(bhie_mem_info->phys_addr); + mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHIE_RXVECADDR_LOW_OFFS, + val); + val = (u32)bhie_mem_info->size; + mhi_reg_write(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHIE_RXVECSIZE_OFFS, + val); + mhi_reg_write_field(mhi_dev_ctxt, bhi_ctxt->bhi_base, BHIE_RXVECDB_OFFS, + BHIE_TXVECDB_SEQNUM_BMSK, BHIE_TXVECDB_SEQNUM_SHFT, + rx_sequence); + + /* trigger device into rddm */ + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Triggering Device into RDDM mode\n"); + mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_SYS_ERR); + i = 0; + + while (timeout--) { + cur_exec = mhi_reg_read(bhi_ctxt->bhi_base, BHI_EXECENV); + state = mhi_get_m_state(mhi_dev_ctxt); + rx_status = mhi_reg_read(bhi_ctxt->bhi_base, + BHIE_RXVECSTATUS_OFFS); + /* if reg. values changed or each sec (udelay(1000)) log it */ + if (cur_exec != prev_exec || state != prev_state || + rx_status != prev_status || !(i & (SZ_1K - 1))) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "EXECENV:0x%x MHISTATE:0x%x RXSTATUS:0x%x\n", + cur_exec, state, rx_status); + prev_exec = cur_exec; + prev_state = state; + prev_status = rx_status; + }; + current_seq = (rx_status & BHIE_TXVECSTATUS_SEQNUM_BMSK) >> + BHIE_TXVECSTATUS_SEQNUM_SHFT; + rx_status = (rx_status & BHIE_TXVECSTATUS_STATUS_BMSK) >> + BHIE_TXVECSTATUS_STATUS_SHFT; + + if ((rx_status == BHIE_TXVECSTATUS_STATUS_XFER_COMPL) && + (current_seq == rx_sequence)) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "rddm transfer completed\n"); + return 0; + } + udelay(BHIE_RDDM_DELAY_TIME_US); + i++; + } + + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "rddm transfer timeout\n"); + + return -EIO; } static int bhi_load_firmware(struct mhi_device_ctxt *mhi_dev_ctxt, @@ -439,7 +530,6 @@ void bhi_firmware_download(struct work_struct *work) struct bhi_ctxt_t *bhi_ctxt; struct bhie_mem_info mem_info; int ret; - long timeout; mhi_dev_ctxt = container_of(work, struct mhi_device_ctxt, bhi_ctxt.fw_load_work); @@ -447,8 +537,16 @@ void bhi_firmware_download(struct work_struct *work) mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enter\n"); - wait_event_interruptible(*mhi_dev_ctxt->mhi_ev_wq.bhi_event, - mhi_dev_ctxt->mhi_state == MHI_STATE_BHI); + ret = wait_event_interruptible_timeout( + *mhi_dev_ctxt->mhi_ev_wq.bhi_event, + mhi_dev_ctxt->mhi_state == MHI_STATE_BHI || + mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT, + msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT)); + if (!ret || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "MHI is not in valid state for firmware download\n"); + return; + } /* PBL image is the first segment in firmware vector table */ mem_info = *bhi_ctxt->fw_table.bhie_mem_info; @@ -462,10 +560,12 @@ void bhi_firmware_download(struct work_struct *work) mhi_init_state_transition(mhi_dev_ctxt, STATE_TRANSITION_RESET); - timeout = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.bhi_event, - mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_BHIE, - msecs_to_jiffies(bhi_ctxt->poll_timeout)); - if (!timeout) { + wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.bhi_event, + mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_BHIE || + mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT, + msecs_to_jiffies(bhi_ctxt->poll_timeout)); + if (mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT || + mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_BHIE) { mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to Enter EXEC_ENV_BHIE\n"); return; diff --git a/drivers/platform/msm/mhi/mhi_bhi.h b/drivers/platform/msm/mhi/mhi_bhi.h index 8f7b3d69347c..8f9bc52bbbe0 100644 --- a/drivers/platform/msm/mhi/mhi_bhi.h +++ b/drivers/platform/msm/mhi/mhi_bhi.h @@ -87,6 +87,7 @@ #define BHI_POLL_SLEEP_TIME_MS 100 #define BHI_POLL_TIMEOUT_MS 2000 +#define BHIE_RDDM_DELAY_TIME_US (1000) int bhi_probe(struct mhi_device_ctxt *mhi_dev_ctxt); void bhi_firmware_download(struct work_struct *work); diff --git a/drivers/platform/msm/mhi/mhi_iface.c b/drivers/platform/msm/mhi/mhi_iface.c index 64a09a2f9fbb..a5936ea5a6aa 100644 --- a/drivers/platform/msm/mhi/mhi_iface.c +++ b/drivers/platform/msm/mhi/mhi_iface.c @@ -153,16 +153,19 @@ static int mhi_pci_probe(struct pci_dev *pcie_device, u32 slot = PCI_SLOT(pcie_device->devfn); unsigned long msi_requested, msi_required; struct msm_pcie_register_event *mhi_pci_link_event; + struct pcie_core_info *core; + int i; + char node[32]; /* Find correct device context based on bdf & dev_id */ mutex_lock(&mhi_device_drv->lock); list_for_each_entry(itr, &mhi_device_drv->head, node) { - struct pcie_core_info *core = &itr->core; - - if (core->domain == domain && - core->bus == bus && - core->dev_id == dev_id && + core = &itr->core; + if (core->domain == domain && core->bus == bus && + (core->dev_id == PCI_ANY_ID || (core->dev_id == dev_id)) && core->slot == slot) { + /* change default dev_id to actual dev_id */ + core->dev_id = dev_id; mhi_dev_ctxt = itr; break; } @@ -171,6 +174,11 @@ static int mhi_pci_probe(struct pci_dev *pcie_device, if (!mhi_dev_ctxt) return -EPROBE_DEFER; + snprintf(node, sizeof(node), "mhi_%04x_%02u.%02u.%02u", + core->dev_id, core->domain, core->bus, core->slot); + mhi_dev_ctxt->mhi_ipc_log = + ipc_log_context_create(MHI_IPC_LOG_PAGES, node, 0); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Processing Domain:%02u Bus:%04u dev:0x%04x slot:%04u\n", domain, bus, dev_id, slot); @@ -280,9 +288,22 @@ static int mhi_pci_probe(struct pci_dev *pcie_device, mutex_lock(&mhi_dev_ctxt->pm_lock); write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); mhi_dev_ctxt->mhi_pm_state = MHI_PM_POR; - ret_val = set_mhi_base_state(mhi_dev_ctxt); + write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); + /* notify all registered clients we probed */ + for (i = 0; i < MHI_MAX_CHANNELS; i++) { + struct mhi_client_handle *client_handle = + mhi_dev_ctxt->client_handle_list[i]; + + if (!client_handle) + continue; + client_handle->dev_id = core->dev_id; + mhi_notify_client(client_handle, MHI_CB_MHI_PROBED); + } + write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); + ret_val = set_mhi_base_state(mhi_dev_ctxt); write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); + if (ret_val) { mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, @@ -344,7 +365,6 @@ static int mhi_plat_probe(struct platform_device *pdev) int r = 0, len; struct mhi_device_ctxt *mhi_dev_ctxt; struct pcie_core_info *core; - char node[32]; struct device_node *of_node = pdev->dev.of_node; u64 address_window[2]; @@ -377,7 +397,7 @@ static int mhi_plat_probe(struct platform_device *pdev) core = &mhi_dev_ctxt->core; r = of_property_read_u32(of_node, "qcom,pci-dev_id", &core->dev_id); if (r) - return r; + core->dev_id = PCI_ANY_ID; r = of_property_read_u32(of_node, "qcom,pci-slot", &core->slot); if (r) @@ -391,14 +411,6 @@ static int mhi_plat_probe(struct platform_device *pdev) if (r) return r; - snprintf(node, sizeof(node), - "mhi_%04x_%02u.%02u.%02u", - core->dev_id, core->domain, core->bus, core->slot); - mhi_dev_ctxt->mhi_ipc_log = - ipc_log_context_create(MHI_IPC_LOG_PAGES, node, 0); - if (!mhi_dev_ctxt->mhi_ipc_log) - pr_err("%s: Error creating ipc_log buffer\n", __func__); - r = of_property_read_u32(of_node, "qcom,mhi-ready-timeout", &mhi_dev_ctxt->poll_reset_timeout_ms); if (r) @@ -407,10 +419,6 @@ static int mhi_plat_probe(struct platform_device *pdev) mhi_dev_ctxt->dev_space.start_win_addr = address_window[0]; mhi_dev_ctxt->dev_space.end_win_addr = address_window[1]; - mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, - "Start Addr:0x%llx End_Addr:0x%llx\n", - mhi_dev_ctxt->dev_space.start_win_addr, - mhi_dev_ctxt->dev_space.end_win_addr); r = of_property_read_u32(of_node, "qcom,bhi-alignment", &mhi_dev_ctxt->bhi_ctxt.alignment); @@ -471,7 +479,6 @@ static int mhi_plat_probe(struct platform_device *pdev) mutex_lock(&mhi_device_drv->lock); list_add_tail(&mhi_dev_ctxt->node, &mhi_device_drv->head); mutex_unlock(&mhi_device_drv->lock); - mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited\n"); return 0; } diff --git a/drivers/platform/msm/mhi/mhi_macros.h b/drivers/platform/msm/mhi/mhi_macros.h index fc0e6f4bc27d..ee0b9b759e0a 100644 --- a/drivers/platform/msm/mhi/mhi_macros.h +++ b/drivers/platform/msm/mhi/mhi_macros.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,8 +27,7 @@ #define CMD_EL_PER_RING 128 #define ELEMENT_GAP 1 #define MHI_EPID 4 -#define MHI_MAX_RESUME_TIMEOUT 5000 -#define MHI_MAX_SUSPEND_TIMEOUT 5000 +#define MHI_MAX_STATE_TRANSITION_TIMEOUT 5000 #define MHI_MAX_CMD_TIMEOUT 500 #define MHI_RPM_AUTOSUSPEND_TMR_VAL_MS 1000 #define MAX_BUF_SIZE 32 @@ -45,7 +44,7 @@ #define MHI_PCIE_DEVICE_ID_ZIRC 0x0301 #define MHI_PCIE_DEVICE_ID_9x55 0x0302 -#define MHI_M2_DEBOUNCE_TMR_MS 10 +#define MHI_M2_DEBOUNCE_TMR_US 10000 #define MHI_EV_DB_INTERVAL 1 diff --git a/drivers/platform/msm/mhi/mhi_main.c b/drivers/platform/msm/mhi/mhi_main.c index 702f9ad5ddfb..739915c4af6f 100644 --- a/drivers/platform/msm/mhi/mhi_main.c +++ b/drivers/platform/msm/mhi/mhi_main.c @@ -562,6 +562,7 @@ int mhi_register_channel(struct mhi_client_handle **client_handle, (*client_handle)->domain = mhi_dev_ctxt->core.domain; (*client_handle)->bus = mhi_dev_ctxt->core.bus; (*client_handle)->slot = mhi_dev_ctxt->core.slot; + (*client_handle)->enabled = false; client_config = (*client_handle)->client_config; client_config->mhi_dev_ctxt = mhi_dev_ctxt; client_config->user_data = client_info->user_data; @@ -588,11 +589,8 @@ int mhi_register_channel(struct mhi_client_handle **client_handle, } if (mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_AMSS && - mhi_dev_ctxt->flags.mhi_initialized) { - mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, - "Exec env is AMSS notify client now chan:%u\n", chan); - mhi_notify_client(*client_handle, MHI_CB_MHI_ENABLED); - } + mhi_dev_ctxt->flags.mhi_initialized) + (*client_handle)->enabled = true; mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Successfuly registered chan:%u\n", chan); @@ -1789,6 +1787,8 @@ int mhi_register_device(struct mhi_device *mhi_device, u32 dev_id = pci_dev->device; u32 slot = PCI_SLOT(pci_dev->devfn); int ret, i; + char node[32]; + struct pcie_core_info *core; of_node = of_parse_phandle(mhi_device->dev->of_node, node_name, 0); if (!of_node) @@ -1801,13 +1801,13 @@ int mhi_register_device(struct mhi_device *mhi_device, mutex_lock(&mhi_device_drv->lock); list_for_each_entry(itr, &mhi_device_drv->head, node) { struct platform_device *pdev = itr->plat_dev; - struct pcie_core_info *core = &itr->core; - if (pdev->dev.of_node == of_node && - core->domain == domain && - core->bus == bus && - core->dev_id == dev_id && - core->slot == slot) { + core = &itr->core; + if (pdev->dev.of_node == of_node && core->domain == domain && + core->bus == bus && core->slot == slot && + (core->dev_id == PCI_ANY_ID || (core->dev_id == dev_id))) { + /* change default dev_id to current dev_id */ + core->dev_id = dev_id; mhi_dev_ctxt = itr; break; } @@ -1818,6 +1818,11 @@ int mhi_register_device(struct mhi_device *mhi_device, if (!mhi_dev_ctxt) return -EPROBE_DEFER; + snprintf(node, sizeof(node), "mhi_%04x_%02u.%02u.%02u", + core->dev_id, core->domain, core->bus, core->slot); + mhi_dev_ctxt->mhi_ipc_log = + ipc_log_context_create(MHI_IPC_LOG_PAGES, node, 0); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Registering Domain:%02u Bus:%04u dev:0x%04x slot:%04u\n", domain, bus, dev_id, slot); @@ -1899,6 +1904,17 @@ int mhi_register_device(struct mhi_device *mhi_device, mhi_dev_ctxt->bhi_ctxt.rddm_size); } + /* notify all the registered clients we probed */ + for (i = 0; i < MHI_MAX_CHANNELS; i++) { + struct mhi_client_handle *client_handle = + mhi_dev_ctxt->client_handle_list[i]; + + if (!client_handle) + continue; + client_handle->dev_id = core->dev_id; + mhi_notify_client(client_handle, MHI_CB_MHI_PROBED); + } + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit success\n"); return 0; } diff --git a/drivers/platform/msm/mhi/mhi_pm.c b/drivers/platform/msm/mhi/mhi_pm.c index caa34eadf8ea..49db99100311 100644 --- a/drivers/platform/msm/mhi/mhi_pm.c +++ b/drivers/platform/msm/mhi/mhi_pm.c @@ -22,6 +22,22 @@ #include "mhi_hwio.h" #include "mhi_bhi.h" +static const char *const mhi_dev_ctrl_str[MHI_DEV_CTRL_MAXCMD] = { + [MHI_DEV_CTRL_INIT] = "INIT", + [MHI_DEV_CTRL_DE_INIT] = "DE-INIT", + [MHI_DEV_CTRL_SUSPEND] = "SUSPEND", + [MHI_DEV_CTRL_RESUME] = "RESUME", + [MHI_DEV_CTRL_POWER_OFF] = "OFF", + [MHI_DEV_CTRL_POWER_ON] = "ON", + [MHI_DEV_CTRL_TRIGGER_RDDM] = "TRIGGER RDDM", + [MHI_DEV_CTRL_RDDM] = "RDDM", + [MHI_DEV_CTRL_RDDM_KERNEL_PANIC] = "RDDM IN PANIC", + [MHI_DEV_CTRL_NOTIFY_LINK_ERROR] = "LD", +}; + +#define TO_MHI_DEV_CTRL_STR(cmd) ((cmd >= MHI_DEV_CTRL_MAXCMD) ? "INVALID" : \ + mhi_dev_ctrl_str[cmd]) + /* Write only sysfs attributes */ static DEVICE_ATTR(MHI_M0, S_IWUSR, NULL, sysfs_init_m0); static DEVICE_ATTR(MHI_M3, S_IWUSR, NULL, sysfs_init_m3); @@ -97,12 +113,14 @@ static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt, mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event, - mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || - mhi_dev_ctxt->mhi_state == MHI_STATE_M1, - msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT)); - if (!r) { + mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || + mhi_dev_ctxt->mhi_state == MHI_STATE_M1 || + mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT, + msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT)); + if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) { mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, - "Failed to get M0||M1 event, timeout, current state:%s\n", + "Failed to get M0||M1 event or LD pm_state:0x%x state:%s\n", + mhi_dev_ctxt->mhi_pm_state, TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); return -EIO; } @@ -122,9 +140,10 @@ static int mhi_pm_initiate_m3(struct mhi_device_ctxt *mhi_dev_ctxt, write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Waiting for M3 completion.\n"); r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m3_event, - mhi_dev_ctxt->mhi_state == MHI_STATE_M3, - msecs_to_jiffies(MHI_MAX_SUSPEND_TIMEOUT)); - if (!r) { + mhi_dev_ctxt->mhi_state == MHI_STATE_M3 || + mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT, + msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT)); + if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) { mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to get M3 event, timeout, current state:%s\n", TO_MHI_STATE_STR(mhi_dev_ctxt->mhi_state)); @@ -158,12 +177,13 @@ static int mhi_pm_initiate_m0(struct mhi_device_ctxt *mhi_dev_ctxt) mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M0); write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); r = wait_event_timeout(*mhi_dev_ctxt->mhi_ev_wq.m0_event, - mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || - mhi_dev_ctxt->mhi_state == MHI_STATE_M1, - msecs_to_jiffies(MHI_MAX_RESUME_TIMEOUT)); - if (!r) { + mhi_dev_ctxt->mhi_state == MHI_STATE_M0 || + mhi_dev_ctxt->mhi_state == MHI_STATE_M1 || + mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT, + msecs_to_jiffies(MHI_MAX_STATE_TRANSITION_TIMEOUT)); + if (!r || mhi_dev_ctxt->mhi_pm_state == MHI_PM_LD_ERR_FATAL_DETECT) { mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, - "Failed to get M0 event, timeout\n"); + "Failed to get M0 event, timeout or LD\n"); r = -EIO; } else r = 0; @@ -295,9 +315,9 @@ static int mhi_pm_slave_mode_power_on(struct mhi_device_ctxt *mhi_dev_ctxt) mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false); read_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); - ret_val = wait_for_completion_timeout(&mhi_dev_ctxt->cmd_complete, - msecs_to_jiffies(timeout)); - if (!ret_val || mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_AMSS) + wait_for_completion_timeout(&mhi_dev_ctxt->cmd_complete, + msecs_to_jiffies(timeout)); + if (mhi_dev_ctxt->dev_exec_env != MHI_EXEC_ENV_AMSS) ret_val = -EIO; else ret_val = 0; @@ -310,6 +330,9 @@ static int mhi_pm_slave_mode_power_on(struct mhi_device_ctxt *mhi_dev_ctxt) unlock_pm_lock: + /* wait for firmware download to complete */ + flush_work(&mhi_dev_ctxt->bhi_ctxt.fw_load_work); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exit with ret:%d\n", ret_val); mutex_unlock(&mhi_dev_ctxt->pm_lock); return ret_val; @@ -537,16 +560,16 @@ void mhi_link_state_cb(struct msm_pcie_notify *notify) } } -int mhi_pm_control_device(struct mhi_device *mhi_device, - enum mhi_dev_ctrl ctrl) +int mhi_pm_control_device(struct mhi_device *mhi_device, enum mhi_dev_ctrl ctrl) { struct mhi_device_ctxt *mhi_dev_ctxt = mhi_device->mhi_dev_ctxt; + unsigned long flags; if (!mhi_dev_ctxt) return -EINVAL; - mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, - "Entered with cmd:%d\n", ctrl); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Entered with cmd:%s\n", + TO_MHI_DEV_CTRL_STR(ctrl)); switch (ctrl) { case MHI_DEV_CTRL_INIT: @@ -560,12 +583,46 @@ int mhi_pm_control_device(struct mhi_device *mhi_device, case MHI_DEV_CTRL_POWER_OFF: mhi_pm_slave_mode_power_off(mhi_dev_ctxt); break; + case MHI_DEV_CTRL_TRIGGER_RDDM: + write_lock_irqsave(&mhi_dev_ctxt->pm_xfer_lock, flags); + if (!MHI_REG_ACCESS_VALID(mhi_dev_ctxt->mhi_pm_state)) { + write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, + flags); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "failed to trigger rddm, no register access in state:0x%x\n", + mhi_dev_ctxt->mhi_pm_state); + return -EIO; + } + mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_SYS_ERR); + write_unlock_irqrestore(&mhi_dev_ctxt->pm_xfer_lock, flags); + break; case MHI_DEV_CTRL_RDDM: return bhi_rddm(mhi_dev_ctxt, false); + case MHI_DEV_CTRL_RDDM_KERNEL_PANIC: + return bhi_rddm(mhi_dev_ctxt, true); case MHI_DEV_CTRL_DE_INIT: - if (mhi_dev_ctxt->mhi_pm_state != MHI_PM_DISABLE) + if (mhi_dev_ctxt->mhi_pm_state != MHI_PM_DISABLE) { + enum MHI_PM_STATE cur_state; + /* + * If bus master calls DE_INIT before calling POWER_OFF + * means a critical failure occurred during POWER_ON + * state transition and external PCIe device may not + * respond to host. Force PM state to PCIe linkdown + * state prior to starting shutdown process to avoid + * accessing PCIe link. + */ + write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); + cur_state = mhi_tryset_pm_state(mhi_dev_ctxt, + MHI_PM_LD_ERR_FATAL_DETECT); + write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); + if (unlikely(cur_state != MHI_PM_LD_ERR_FATAL_DETECT)) { + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "Failed to transition to state 0x%x from 0x%x\n", + MHI_PM_LD_ERR_FATAL_DETECT, cur_state); + } process_disable_transition(MHI_PM_SHUTDOWN_PROCESS, mhi_dev_ctxt); + } bhi_exit(mhi_dev_ctxt); break; case MHI_DEV_CTRL_NOTIFY_LINK_ERROR: @@ -580,6 +637,12 @@ int mhi_pm_control_device(struct mhi_device *mhi_device, mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Failed to transition to state 0x%x from 0x%x\n", MHI_PM_LD_ERR_FATAL_DETECT, cur_state); + + /* wake up all threads that's waiting for state change events */ + complete(&mhi_dev_ctxt->cmd_complete); + wake_up_interruptible(mhi_dev_ctxt->mhi_ev_wq.bhi_event); + wake_up(mhi_dev_ctxt->mhi_ev_wq.m0_event); + wake_up(mhi_dev_ctxt->mhi_ev_wq.m3_event); break; } default: diff --git a/drivers/platform/msm/mhi/mhi_states.c b/drivers/platform/msm/mhi/mhi_states.c index c0c23c4e0756..ea2a91bd2d06 100644 --- a/drivers/platform/msm/mhi/mhi_states.c +++ b/drivers/platform/msm/mhi/mhi_states.c @@ -147,7 +147,8 @@ void mhi_set_m_state(struct mhi_device_ctxt *mhi_dev_ctxt, * M1 -> M3_ENTER --> M3 * L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS --> POR * L2: SHUTDOWN_PROCESS -> DISABLE -> SSR_PENDING (via SSR Notification only) - * L3: LD_ERR_FATAL_DETECT -> SHUTDOWN_PROCESS + * L3: LD_ERR_FATAL_DETECT <--> LD_ERR_FATAL_DETECT + * LD_ERR_FATAL_DETECT -> SHUTDOWN_PROCESS */ static const struct mhi_pm_transitions const mhi_state_transitions[] = { /* L0 States */ @@ -216,7 +217,7 @@ static const struct mhi_pm_transitions const mhi_state_transitions[] = { /* L3 States */ { MHI_PM_LD_ERR_FATAL_DETECT, - MHI_PM_SHUTDOWN_PROCESS + MHI_PM_LD_ERR_FATAL_DETECT | MHI_PM_SHUTDOWN_PROCESS }, /* From SSR notification only */ { @@ -430,7 +431,7 @@ void process_m1_transition(struct work_struct *work) mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_M2); write_unlock_irq(&mhi_dev_ctxt->pm_xfer_lock); - msleep(MHI_M2_DEBOUNCE_TMR_MS); + usleep_range(MHI_M2_DEBOUNCE_TMR_US, MHI_M2_DEBOUNCE_TMR_US + 50); write_lock_irq(&mhi_dev_ctxt->pm_xfer_lock); /* During DEBOUNCE Time We could be receiving M0 Event */ @@ -590,26 +591,26 @@ static int process_reset_transition( } static void enable_clients(struct mhi_device_ctxt *mhi_dev_ctxt, - enum MHI_EXEC_ENV exec_env) + enum MHI_EXEC_ENV exec_env) { struct mhi_client_handle *client_handle = NULL; - struct mhi_cb_info cb_info; - int i = 0, r = 0; - struct mhi_chan_info chan_info; - - cb_info.cb_reason = MHI_CB_MHI_ENABLED; + struct mhi_chan_info *chan_info; + int i = 0; mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Enabling Clients, exec env %d.\n", exec_env); + for (i = 0; i < MHI_MAX_CHANNELS; ++i) { - if (!VALID_CHAN_NR(i)) + if (!mhi_dev_ctxt->client_handle_list[i]) continue; + client_handle = mhi_dev_ctxt->client_handle_list[i]; - r = get_chan_props(mhi_dev_ctxt, i, &chan_info); - if (!r && client_handle && - exec_env == GET_CHAN_PROPS(CHAN_BRINGUP_STAGE, - chan_info.flags)) + chan_info = &client_handle->client_config->chan_info; + if (exec_env == GET_CHAN_PROPS(CHAN_BRINGUP_STAGE, + chan_info->flags)) { + client_handle->enabled = true; mhi_notify_client(client_handle, MHI_CB_MHI_ENABLED); + } } mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Done.\n"); diff --git a/drivers/platform/msm/mhi_uci/mhi_uci.c b/drivers/platform/msm/mhi_uci/mhi_uci.c index 3191ec065a95..4563810d7e5b 100644 --- a/drivers/platform/msm/mhi_uci/mhi_uci.c +++ b/drivers/platform/msm/mhi_uci/mhi_uci.c @@ -122,6 +122,7 @@ struct uci_client { atomic_t out_pkt_pend_ack; atomic_t completion_ack; struct mhi_uci_ctxt_t *uci_ctxt; + struct cdev cdev; bool enabled; void *uci_ipc_log; }; @@ -132,7 +133,6 @@ struct mhi_uci_ctxt_t { struct uci_client client_handles[MHI_SOFTWARE_CLIENT_LIMIT]; dev_t dev_t; struct mutex ctrl_mutex; - struct cdev cdev[MHI_SOFTWARE_CLIENT_LIMIT]; struct uci_client *ctrl_client; }; @@ -246,6 +246,7 @@ static int mhi_uci_client_release(struct inode *mhi_inode, static unsigned int mhi_uci_client_poll(struct file *file, poll_table *wait); static long mhi_uci_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +static int mhi_uci_create_device(struct uci_client *uci_client); static struct mhi_uci_drv_ctxt mhi_uci_drv_ctxt; @@ -1174,6 +1175,14 @@ static void uci_xfer_cb(struct mhi_cb_info *cb_info) uci_handle = cb_info->result->user_data; switch (cb_info->cb_reason) { + case MHI_CB_MHI_PROBED: + /* If it's outbound channel create the node */ + mutex_lock(&uci_handle->client_lock); + if (!uci_handle->dev && + cb_info->chan == uci_handle->out_attr.chan_id) + mhi_uci_create_device(uci_handle); + mutex_unlock(&uci_handle->client_lock); + break; case MHI_CB_MHI_ENABLED: uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO, "MHI enabled CB received for chan %d\n", @@ -1295,17 +1304,65 @@ static const struct file_operations mhi_uci_client_fops = { .unlocked_ioctl = mhi_uci_ctl_ioctl, }; +static int mhi_uci_create_device(struct uci_client *uci_client) +{ + struct mhi_uci_ctxt_t *uci_ctxt = uci_client->uci_ctxt; + char node_name[32]; + int index = uci_client - uci_ctxt->client_handles; + int ret; + + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_INFO, + "Creating dev node %04x_%02u.%02u.%02u_pipe%d\n", + uci_client->out_attr.mhi_handle->dev_id, + uci_client->out_attr.mhi_handle->domain, + uci_client->out_attr.mhi_handle->bus, + uci_client->out_attr.mhi_handle->slot, + uci_client->out_attr.chan_id); + + cdev_init(&uci_client->cdev, &mhi_uci_client_fops); + uci_client->cdev.owner = THIS_MODULE; + ret = cdev_add(&uci_client->cdev, uci_ctxt->dev_t + index, 1); + if (ret) { + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_ERROR, + "Failed to add cdev %d, ret:%d\n", index, ret); + return ret; + } + uci_client->dev = device_create(mhi_uci_drv_ctxt.mhi_uci_class, NULL, + uci_ctxt->dev_t + index, NULL, + DEVICE_NAME "_%04x_%02u.%02u.%02u%s%d", + uci_client->out_attr.mhi_handle->dev_id, + uci_client->out_attr.mhi_handle->domain, + uci_client->out_attr.mhi_handle->bus, + uci_client->out_attr.mhi_handle->slot, + "_pipe_", + uci_client->out_attr.chan_id); + if (IS_ERR(uci_client->dev)) { + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_ERROR, + "Failed to add cdev %d\n", IS_ERR(uci_client->dev)); + cdev_del(&uci_client->cdev); + return -EIO; + } + + /* dev node created successfully, create logging buffer */ + snprintf(node_name, sizeof(node_name), "mhi_uci_%04x_%02u.%02u.%02u_%d", + uci_client->out_attr.mhi_handle->dev_id, + uci_client->out_attr.mhi_handle->domain, + uci_client->out_attr.mhi_handle->bus, + uci_client->out_attr.mhi_handle->slot, + uci_client->out_attr.chan_id); + uci_client->uci_ipc_log = ipc_log_context_create(MHI_UCI_IPC_LOG_PAGES, + node_name, 0); + + return 0; +} + static int mhi_uci_probe(struct platform_device *pdev) { struct mhi_uci_ctxt_t *uci_ctxt; int ret_val; int i; - char node_name[32]; - uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, - UCI_DBG_INFO, - "Entered with pdev:%p\n", - pdev); + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_INFO, "Entered\n"); if (mhi_is_device_ready(&pdev->dev, "qcom,mhi") == false) return -EPROBE_DEFER; @@ -1326,128 +1383,67 @@ static int mhi_uci_probe(struct platform_device *pdev) uci_ctxt->pdev = pdev; mutex_init(&uci_ctxt->ctrl_mutex); - uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, - UCI_DBG_INFO, + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_INFO, "Setting up channel attributes\n"); ret_val = uci_init_client_attributes(uci_ctxt, pdev->dev.of_node); if (ret_val) { - uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, - UCI_DBG_ERROR, + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_ERROR, "Failed to init client attributes\n"); return -EIO; } - uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, - UCI_DBG_INFO, + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_INFO, + "Allocating char devices\n"); + ret_val = alloc_chrdev_region(&uci_ctxt->dev_t, 0, + MHI_SOFTWARE_CLIENT_LIMIT, + DEVICE_NAME); + if (IS_ERR_VALUE(ret_val)) { + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_ERROR, + "Failed to alloc char devs, ret 0x%x\n", ret_val); + return ret_val; + } + + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_INFO, "Registering for MHI events\n"); for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; ++i) { struct uci_client *uci_client = &uci_ctxt->client_handles[i]; uci_client->uci_ctxt = uci_ctxt; mutex_init(&uci_client->client_lock); - if (uci_client->in_attr.uci_ownership) { - ret_val = mhi_register_client(uci_client, - &pdev->dev); + if (!uci_client->in_attr.uci_ownership) + continue; + ret_val = mhi_register_client(uci_client, &pdev->dev); + if (ret_val) { + uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, + UCI_DBG_CRITICAL, + "Failed to reg client %d ret %d\n", + ret_val, i); + return -EIO; + } + + mutex_lock(&uci_client->client_lock); + /* If we have device id, create the node now */ + if (uci_client->out_attr.mhi_handle->dev_id != PCI_ANY_ID) { + ret_val = mhi_uci_create_device(uci_client); if (ret_val) { uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, UCI_DBG_CRITICAL, - "Failed to reg client %d ret %d\n", - ret_val, - i); - + "Failed to create device node, ret:%d\n", + ret_val); + mutex_unlock(&uci_client->client_lock); return -EIO; } - snprintf(node_name, - sizeof(node_name), - "mhi_uci_%04x_%02u.%02u.%02u_%d", - uci_client->out_attr.mhi_handle->dev_id, - uci_client->out_attr.mhi_handle->domain, - uci_client->out_attr.mhi_handle->bus, - uci_client->out_attr.mhi_handle->slot, - uci_client->out_attr.chan_id); - uci_client->uci_ipc_log = ipc_log_context_create - (MHI_UCI_IPC_LOG_PAGES, - node_name, - 0); } + mutex_unlock(&uci_client->client_lock); } - uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, - UCI_DBG_INFO, - "Allocating char devices\n"); - ret_val = alloc_chrdev_region(&uci_ctxt->dev_t, - 0, - MHI_SOFTWARE_CLIENT_LIMIT, - DEVICE_NAME); - if (IS_ERR_VALUE(ret_val)) { - uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, - UCI_DBG_ERROR, - "Failed to alloc char devs, ret 0x%x\n", ret_val); - goto failed_char_alloc; - } - - uci_log(mhi_uci_drv_ctxt.mhi_uci_ipc_log, - UCI_DBG_INFO, - "Setting up device nodes. for dev_t: 0x%x major:0x%x\n", - uci_ctxt->dev_t, - MAJOR(uci_ctxt->dev_t)); - for (i = 0; i < MHI_SOFTWARE_CLIENT_LIMIT; ++i) { - struct uci_client *uci_client = &uci_ctxt->client_handles[i]; - - if (uci_client->in_attr.uci_ownership) { - cdev_init(&uci_ctxt->cdev[i], &mhi_uci_client_fops); - uci_ctxt->cdev[i].owner = THIS_MODULE; - ret_val = cdev_add(&uci_ctxt->cdev[i], - uci_ctxt->dev_t + i, 1); - if (IS_ERR_VALUE(ret_val)) { - uci_log(uci_client->uci_ipc_log, - UCI_DBG_ERROR, - "Failed to add cdev %d, ret 0x%x\n", - i, ret_val); - goto failed_char_add; - } - uci_client->dev = - device_create(mhi_uci_drv_ctxt.mhi_uci_class, - NULL, - uci_ctxt->dev_t + i, - NULL, - DEVICE_NAME "_%04x_%02u.%02u.%02u%s%d", - uci_client->out_attr.mhi_handle->dev_id, - uci_client->out_attr.mhi_handle->domain, - uci_client->out_attr.mhi_handle->bus, - uci_client->out_attr.mhi_handle->slot, - "_pipe_", - uci_client->out_attr.chan_id); - if (IS_ERR(uci_client->dev)) { - uci_log(uci_client->uci_ipc_log, - UCI_DBG_ERROR, - "Failed to add cdev %d\n", i); - cdev_del(&uci_ctxt->cdev[i]); - ret_val = -EIO; - goto failed_device_create; - } - } - } platform_set_drvdata(pdev, uci_ctxt); mutex_lock(&mhi_uci_drv_ctxt.list_lock); list_add_tail(&uci_ctxt->node, &mhi_uci_drv_ctxt.head); mutex_unlock(&mhi_uci_drv_ctxt.list_lock); - return 0; - -failed_char_add: -failed_device_create: - while (--i >= 0) { - cdev_del(&uci_ctxt->cdev[i]); - device_destroy(mhi_uci_drv_ctxt.mhi_uci_class, - MKDEV(MAJOR(uci_ctxt->dev_t), i)); - }; - - unregister_chrdev_region(MAJOR(uci_ctxt->dev_t), - MHI_SOFTWARE_CLIENT_LIMIT); -failed_char_alloc: - return ret_val; + return 0; }; static int mhi_uci_remove(struct platform_device *pdev) @@ -1462,7 +1458,7 @@ static int mhi_uci_remove(struct platform_device *pdev) if (uci_client->in_attr.uci_ownership) { mhi_deregister_channel(uci_client->out_attr.mhi_handle); mhi_deregister_channel(uci_client->in_attr.mhi_handle); - cdev_del(&uci_ctxt->cdev[i]); + cdev_del(&uci_client->cdev); device_destroy(mhi_uci_drv_ctxt.mhi_uci_class, MKDEV(MAJOR(uci_ctxt->dev_t), i)); } diff --git a/drivers/platform/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c index 53c85773b377..4f7d27db8dee 100644 --- a/drivers/platform/msm/msm_ext_display.c +++ b/drivers/platform/msm/msm_ext_display.c @@ -221,6 +221,11 @@ static int msm_ext_disp_process_display(struct msm_ext_disp *ext_disp, goto end; } + if (state == EXT_DISPLAY_CABLE_CONNECT) + ext_disp->current_disp = type; + else + ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX; + ret = msm_ext_disp_send_cable_notification(ext_disp, state); /* positive ret value means audio node was switched */ @@ -287,7 +292,6 @@ static bool msm_ext_disp_validate_connect(struct msm_ext_disp *ext_disp, if (ext_disp->current_disp != type) return false; end: - ext_disp->current_disp = type; return true; } @@ -369,8 +373,6 @@ static int msm_ext_disp_hpd(struct platform_device *pdev, msm_ext_disp_process_audio(ext_disp, type, state, flags); msm_ext_disp_update_audio_ops(ext_disp, type, state, flags); msm_ext_disp_process_display(ext_disp, type, state, flags); - - ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX; } pr_debug("Hpd (%d) for display (%s)\n", state, diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c index 2f11c6dd7e05..ddb2388c5006 100644 --- a/drivers/platform/msm/sps/sps.c +++ b/drivers/platform/msm/sps/sps.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016 , The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -67,6 +67,7 @@ static char *debugfs_buf; static u32 debugfs_buf_size; static u32 debugfs_buf_used; static int wraparound; +static struct mutex sps_debugfs_lock; struct dentry *dent; struct dentry *dfile_info; @@ -85,6 +86,7 @@ static struct sps_bam *phy2bam(phys_addr_t phys_addr); /* record debug info for debugfs */ void sps_debugfs_record(const char *msg) { + mutex_lock(&sps_debugfs_lock); if (debugfs_record_enabled) { if (debugfs_buf_used + MAX_MSG_LEN >= debugfs_buf_size) { debugfs_buf_used = 0; @@ -98,6 +100,7 @@ void sps_debugfs_record(const char *msg) debugfs_buf_size - debugfs_buf_used, "\n**** end line of sps log ****\n\n"); } + mutex_unlock(&sps_debugfs_lock); } /* read the recorded debug info to userspace */ @@ -107,6 +110,7 @@ static ssize_t sps_read_info(struct file *file, char __user *ubuf, int ret = 0; int size; + mutex_lock(&sps_debugfs_lock); if (debugfs_record_enabled) { if (wraparound) size = debugfs_buf_size - MAX_MSG_LEN; @@ -116,6 +120,7 @@ static ssize_t sps_read_info(struct file *file, char __user *ubuf, ret = simple_read_from_buffer(ubuf, count, ppos, debugfs_buf, size); } + mutex_unlock(&sps_debugfs_lock); return ret; } @@ -161,11 +166,13 @@ static ssize_t sps_set_info(struct file *file, const char __user *buf, new_buf_size = buf_size_kb * SZ_1K; + mutex_lock(&sps_debugfs_lock); if (debugfs_record_enabled) { if (debugfs_buf_size == new_buf_size) { /* need do nothing */ pr_info("sps:debugfs: input buffer size " "is the same as before.\n"); + mutex_unlock(&sps_debugfs_lock); return count; } else { /* release the current buffer */ @@ -185,12 +192,14 @@ static ssize_t sps_set_info(struct file *file, const char __user *buf, if (!debugfs_buf) { debugfs_buf_size = 0; pr_err("sps:fail to allocate memory for debug_fs.\n"); + mutex_unlock(&sps_debugfs_lock); return -ENOMEM; } debugfs_buf_used = 0; wraparound = false; debugfs_record_enabled = true; + mutex_unlock(&sps_debugfs_lock); return count; } @@ -239,6 +248,7 @@ static ssize_t sps_set_logging_option(struct file *file, const char __user *buf, return count; } + mutex_lock(&sps_debugfs_lock); if (((option == 0) || (option == 2)) && ((logging_option == 1) || (logging_option == 3))) { debugfs_record_enabled = false; @@ -250,6 +260,7 @@ static ssize_t sps_set_logging_option(struct file *file, const char __user *buf, } logging_option = option; + mutex_unlock(&sps_debugfs_lock); return count; } @@ -587,6 +598,8 @@ static void sps_debugfs_init(void) goto bam_log_level_err; } + mutex_init(&sps_debugfs_lock); + return; bam_log_level_err: diff --git a/drivers/platform/msm/sps/spsi.h b/drivers/platform/msm/sps/spsi.h index 1b4ca69bee16..38ee76be13c1 100644 --- a/drivers/platform/msm/sps/spsi.h +++ b/drivers/platform/msm/sps/spsi.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -145,11 +145,6 @@ extern u8 print_limit_option; pr_info(msg, ##args); \ } \ } while (0) -#define SPS_DEBUGFS(msg, args...) do { \ - char buf[MAX_MSG_LEN]; \ - snprintf(buf, MAX_MSG_LEN, msg"\n", ##args); \ - sps_debugfs_record(buf); \ - } while (0) #define SPS_ERR(dev, msg, args...) do { \ if (logging_option != 1) { \ if (unlikely(print_limit_option > 2)) \ @@ -157,8 +152,6 @@ extern u8 print_limit_option; else \ pr_err(msg, ##args); \ } \ - if (unlikely(debugfs_record_enabled)) \ - SPS_DEBUGFS(msg, ##args); \ SPS_IPC(3, dev, msg, args); \ } while (0) #define SPS_INFO(dev, msg, args...) do { \ @@ -168,8 +161,6 @@ extern u8 print_limit_option; else \ pr_info(msg, ##args); \ } \ - if (unlikely(debugfs_record_enabled)) \ - SPS_DEBUGFS(msg, ##args); \ SPS_IPC(3, dev, msg, args); \ } while (0) #define SPS_DBG(dev, msg, args...) do { \ @@ -181,8 +172,6 @@ extern u8 print_limit_option; pr_info(msg, ##args); \ } else \ pr_debug(msg, ##args); \ - if (unlikely(debugfs_record_enabled)) \ - SPS_DEBUGFS(msg, ##args); \ if (dev) { \ if ((dev)->ipc_loglevel <= 0) \ SPS_IPC(0, dev, msg, args); \ @@ -197,8 +186,6 @@ extern u8 print_limit_option; pr_info(msg, ##args); \ } else \ pr_debug(msg, ##args); \ - if (unlikely(debugfs_record_enabled)) \ - SPS_DEBUGFS(msg, ##args); \ if (dev) { \ if ((dev)->ipc_loglevel <= 1) \ SPS_IPC(1, dev, msg, args); \ @@ -213,8 +200,6 @@ extern u8 print_limit_option; pr_info(msg, ##args); \ } else \ pr_debug(msg, ##args); \ - if (unlikely(debugfs_record_enabled)) \ - SPS_DEBUGFS(msg, ##args); \ if (dev) { \ if ((dev)->ipc_loglevel <= 2) \ SPS_IPC(2, dev, msg, args); \ @@ -229,8 +214,6 @@ extern u8 print_limit_option; pr_info(msg, ##args); \ } else \ pr_debug(msg, ##args); \ - if (unlikely(debugfs_record_enabled)) \ - SPS_DEBUGFS(msg, ##args); \ if (dev) { \ if ((dev)->ipc_loglevel <= 3) \ SPS_IPC(3, dev, msg, args); \ diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c index 5cb017fdf2d3..a151d0c7a770 100644 --- a/drivers/platform/msm/usb_bam.c +++ b/drivers/platform/msm/usb_bam.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -3100,79 +3100,6 @@ static int enable_usb_bam(struct platform_device *pdev) return 0; } -static ssize_t -usb_bam_show_inactivity_timer(struct device *dev, struct device_attribute *attr, - char *buf) -{ - char *buff = buf; - int i; - - for (i = 0; i < ARRAY_SIZE(bam_enable_strings); i++) { - buff += snprintf(buff, PAGE_SIZE, "%s: %dms\n", - bam_enable_strings[i], - msm_usb_bam[i].inactivity_timer_ms); - } - - return buff - buf; -} - -static ssize_t usb_bam_store_inactivity_timer(struct device *dev, - struct device_attribute *attr, - const char *buff, size_t count) -{ - char buf[USB_BAM_MAX_STR_LEN]; - char *trimmed_buf, *bam_str, *bam_name, *timer; - int timer_d; - int bam; - - if (strnstr(buff, "help", USB_BAM_MAX_STR_LEN)) { - pr_info("Usage: <bam_name> <ms>,<bam_name> <ms>,...\n"); - pr_info("\tbam_name: [%s, %s, %s]\n", - bam_enable_strings[DWC3_CTRL], - bam_enable_strings[CI_CTRL], - bam_enable_strings[HSIC_CTRL]); - pr_info("\tms: time in ms. Use 0 to disable timer\n"); - return count; - } - - strlcpy(buf, buff, sizeof(buf)); - trimmed_buf = strim(buf); - - while (trimmed_buf) { - bam_str = strsep(&trimmed_buf, ","); - if (bam_str) { - bam_name = strsep(&bam_str, " "); - bam = get_bam_type_from_core_name(bam_name); - if (bam < 0 || bam >= MAX_BAMS) { - log_event_err("%s: Invalid bam, type=%d ,name=%s\n", - __func__, bam, bam_name); - return -EINVAL; - } - - timer = strsep(&bam_str, " "); - - if (!timer) - continue; - - sscanf(timer, "%d", &timer_d); - - /* Apply new timer setting if bam has running pipes */ - if (msm_usb_bam[bam].inactivity_timer_ms != timer_d) { - msm_usb_bam[bam].inactivity_timer_ms = timer_d; - if (msm_usb_bam[bam].pipes_enabled_per_bam > 0 - && !info[bam].in_lpm) - usb_bam_set_inactivity_timer(bam); - } - } - } - - return count; -} - -static DEVICE_ATTR(inactivity_timer, S_IWUSR | S_IRUSR, - usb_bam_show_inactivity_timer, - usb_bam_store_inactivity_timer); - static int usb_bam_panic_notifier(struct notifier_block *this, unsigned long event, void *ptr) { @@ -3221,12 +3148,6 @@ static int usb_bam_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "usb_bam_probe\n"); - ret = device_create_file(&pdev->dev, &dev_attr_inactivity_timer); - if (ret) { - dev_err(&pdev->dev, "failed to create fs node\n"); - return ret; - } - io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!io_res) { dev_err(&pdev->dev, "missing BAM memory resource\n"); diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index bd77e55bafc6..0dd6017245fa 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -46,7 +46,7 @@ static ssize_t power_supply_show_property(struct device *dev, static char *type_text[] = { "Unknown", "Battery", "UPS", "Mains", "USB", "USB_DCP", "USB_CDP", "USB_ACA", "USB_HVDCP", "USB_HVDCP_3", "USB_PD", - "Wireless", "BMS", "Parallel", "Main", "Wipower", + "Wireless", "USB_FLOAT", "BMS", "Parallel", "Main", "Wipower", "TYPEC", "TYPEC_UFP", "TYPEC_DFP" }; static char *status_text[] = { diff --git a/drivers/power/qcom/debug_core.c b/drivers/power/qcom/debug_core.c index ccef04ae9eb2..51b6d63fe994 100644 --- a/drivers/power/qcom/debug_core.c +++ b/drivers/power/qcom/debug_core.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -288,8 +288,8 @@ static const struct file_operations msm_core_ptable_ops = { int msm_core_debug_init(void) { - struct dentry *dir; - struct dentry *file; + struct dentry *dir = NULL; + struct dentry *file = NULL; int i; msm_core_data = get_cpu_pwr_stats(); diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index acea4f213484..e17c65c1e4e7 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -47,7 +47,7 @@ #define SCM_EDLOAD_MODE 0X01 #define SCM_DLOAD_CMD 0x10 #define SCM_DLOAD_MINIDUMP 0X20 - +#define SCM_DLOAD_BOTHDUMPS (SCM_DLOAD_MINIDUMP | SCM_DLOAD_FULLDUMP) static int restart_mode; static void *restart_reason; @@ -488,7 +488,8 @@ static ssize_t show_dload_mode(struct kobject *kobj, struct attribute *attr, char *buf) { return scnprintf(buf, PAGE_SIZE, "DLOAD dump type: %s\n", - (dload_type == SCM_DLOAD_MINIDUMP) ? "mini" : "full"); + (dload_type == SCM_DLOAD_BOTHDUMPS) ? "both" : + ((dload_type == SCM_DLOAD_MINIDUMP) ? "mini" : "full")); } static size_t store_dload_mode(struct kobject *kobj, struct attribute *attr, @@ -502,8 +503,16 @@ static size_t store_dload_mode(struct kobject *kobj, struct attribute *attr, return -ENODEV; } dload_type = SCM_DLOAD_MINIDUMP; - } else { - pr_err("Invalid value. Use 'full' or 'mini'\n"); + } else if (sysfs_streq(buf, "both")) { + if (!minidump_enabled) { + pr_err("Minidump not enabled, setting fulldump only\n"); + dload_type = SCM_DLOAD_FULLDUMP; + return count; + } + dload_type = SCM_DLOAD_BOTHDUMPS; + } else{ + pr_err("Invalid Dump setup request..\n"); + pr_err("Supported dumps:'full', 'mini', or 'both'\n"); return -EINVAL; } diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index 38f9f762ccc7..5e8cc84fbfbf 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -550,8 +550,6 @@ static int usb_icl_vote_callback(struct votable *votable, void *data, power_supply_set_property(chip->main_psy, POWER_SUPPLY_PROP_CURRENT_MAX, &pval); - /* wait for ICL change */ - msleep(20); } /* set the effective ICL */ diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h index 25c9d0251cf1..947108c1410e 100644 --- a/drivers/power/supply/qcom/fg-core.h +++ b/drivers/power/supply/qcom/fg-core.h @@ -51,9 +51,12 @@ #define PROFILE_LOAD "fg_profile_load" #define DELTA_SOC "fg_delta_soc" -/* Delta BSOC votable reasons */ +/* Delta BSOC irq votable reasons */ #define DELTA_BSOC_IRQ_VOTER "fg_delta_bsoc_irq" +/* Battery missing irq votable reasons */ +#define BATT_MISS_IRQ_VOTER "fg_batt_miss_irq" + #define DEBUG_PRINT_BUFFER_SIZE 64 /* 3 byte address + 1 space character */ #define ADDR_LEN 4 @@ -219,6 +222,12 @@ enum slope_limit_status { SLOPE_LIMIT_NUM_COEFFS, }; +enum esr_timer_config { + TIMER_RETRY = 0, + TIMER_MAX, + NUM_ESR_TIMERS, +}; + /* DT parameters for FG device */ struct fg_dt_props { bool force_load_profile; @@ -234,9 +243,9 @@ struct fg_dt_props { int recharge_soc_thr; int recharge_volt_thr_mv; int rsense_sel; - int esr_timer_charging; - int esr_timer_awake; - int esr_timer_asleep; + int esr_timer_charging[NUM_ESR_TIMERS]; + int esr_timer_awake[NUM_ESR_TIMERS]; + int esr_timer_asleep[NUM_ESR_TIMERS]; int rconn_mohms; int esr_clamp_mohms; int cl_start_soc; @@ -355,6 +364,7 @@ struct fg_chip { struct fg_irq_info *irqs; struct votable *awake_votable; struct votable *delta_bsoc_irq_en_votable; + struct votable *batt_miss_irq_en_votable; struct fg_sram_param *sp; struct fg_alg_flag *alg_flags; int *debug_mask; @@ -385,6 +395,7 @@ struct fg_chip { int maint_soc; int delta_soc; int last_msoc; + int esr_timer_charging_default[NUM_ESR_TIMERS]; enum slope_limit_status slope_limit_sts; bool profile_available; bool profile_loaded; @@ -460,6 +471,7 @@ extern void dump_sram(u8 *buf, int addr, int len); extern int64_t twos_compliment_extend(int64_t val, int s_bit_pos); extern s64 fg_float_decode(u16 val); extern bool is_input_present(struct fg_chip *chip); +extern bool is_qnovo_en(struct fg_chip *chip); extern void fg_circ_buf_add(struct fg_circ_buf *, int); extern void fg_circ_buf_clr(struct fg_circ_buf *); extern int fg_circ_buf_avg(struct fg_circ_buf *, int *); diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c index f2395b6ba4ab..9635044e02a5 100644 --- a/drivers/power/supply/qcom/fg-util.c +++ b/drivers/power/supply/qcom/fg-util.c @@ -106,14 +106,17 @@ static struct fg_dbgfs dbgfs_data = { static bool is_usb_present(struct fg_chip *chip) { union power_supply_propval pval = {0, }; + int rc; if (!chip->usb_psy) chip->usb_psy = power_supply_get_by_name("usb"); - if (chip->usb_psy) - power_supply_get_property(chip->usb_psy, - POWER_SUPPLY_PROP_PRESENT, &pval); - else + if (!chip->usb_psy) + return false; + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + if (rc < 0) return false; return pval.intval != 0; @@ -122,14 +125,17 @@ static bool is_usb_present(struct fg_chip *chip) static bool is_dc_present(struct fg_chip *chip) { union power_supply_propval pval = {0, }; + int rc; if (!chip->dc_psy) chip->dc_psy = power_supply_get_by_name("dc"); - if (chip->dc_psy) - power_supply_get_property(chip->dc_psy, - POWER_SUPPLY_PROP_PRESENT, &pval); - else + if (!chip->dc_psy) + return false; + + rc = power_supply_get_property(chip->dc_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + if (rc < 0) return false; return pval.intval != 0; @@ -140,6 +146,25 @@ bool is_input_present(struct fg_chip *chip) return is_usb_present(chip) || is_dc_present(chip); } +bool is_qnovo_en(struct fg_chip *chip) +{ + union power_supply_propval pval = {0, }; + int rc; + + if (!chip->batt_psy) + chip->batt_psy = power_supply_get_by_name("battery"); + + if (!chip->batt_psy) + return false; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, &pval); + if (rc < 0) + return false; + + return pval.intval != 0; +} + #define EXPONENT_SHIFT 11 #define EXPONENT_OFFSET -9 #define MANTISSA_SIGN_BIT 10 diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c index 26648595c55c..e03681ec13cc 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen3.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c @@ -491,7 +491,7 @@ static void fg_encode_default(struct fg_sram_param *sp, int i, mask = 0xff; int64_t temp; - temp = DIV_ROUND_CLOSEST(val * sp[id].numrtr, sp[id].denmtr); + temp = (int64_t)div_s64((s64)val * sp[id].numrtr, sp[id].denmtr); pr_debug("temp: %llx id: %d, val: %d, buf: [ ", temp, id, val); for (i = 0; i < sp[id].len; i++) { buf[i] = temp & mask; @@ -904,6 +904,7 @@ out: return ret; } + vote(chip->batt_miss_irq_en_votable, BATT_MISS_IRQ_VOTER, true, 0); return rc; } @@ -1025,12 +1026,15 @@ static inline void get_esr_meas_current(int curr_ma, u8 *val) *val <<= ESR_PULL_DOWN_IVAL_SHIFT; } -static int fg_set_esr_timer(struct fg_chip *chip, int cycles, bool charging, - int flags) +static int fg_set_esr_timer(struct fg_chip *chip, int cycles_init, + int cycles_max, bool charging, int flags) { u8 buf[2]; int rc, timer_max, timer_init; + if (cycles_init < 0 || cycles_max < 0) + return 0; + if (charging) { timer_max = FG_SRAM_ESR_TIMER_CHG_MAX; timer_init = FG_SRAM_ESR_TIMER_CHG_INIT; @@ -1039,7 +1043,7 @@ static int fg_set_esr_timer(struct fg_chip *chip, int cycles, bool charging, timer_init = FG_SRAM_ESR_TIMER_DISCHG_INIT; } - fg_encode(chip->sp, timer_max, cycles, buf); + fg_encode(chip->sp, timer_max, cycles_max, buf); rc = fg_sram_write(chip, chip->sp[timer_max].addr_word, chip->sp[timer_max].addr_byte, buf, @@ -1050,7 +1054,7 @@ static int fg_set_esr_timer(struct fg_chip *chip, int cycles, bool charging, return rc; } - fg_encode(chip->sp, timer_init, cycles, buf); + fg_encode(chip->sp, timer_init, cycles_init, buf); rc = fg_sram_write(chip, chip->sp[timer_init].addr_word, chip->sp[timer_init].addr_byte, buf, @@ -1061,6 +1065,8 @@ static int fg_set_esr_timer(struct fg_chip *chip, int cycles, bool charging, return rc; } + fg_dbg(chip, FG_STATUS, "esr_%s_timer set to %d/%d\n", + charging ? "charging" : "discharging", cycles_init, cycles_max); return 0; } @@ -1098,6 +1104,25 @@ static void fg_notify_charger(struct fg_chip *chip) fg_dbg(chip, FG_STATUS, "Notified charger on float voltage and FCC\n"); } +static int fg_batt_miss_irq_en_cb(struct votable *votable, void *data, + int enable, const char *client) +{ + struct fg_chip *chip = data; + + if (!chip->irqs[BATT_MISSING_IRQ].irq) + return 0; + + if (enable) { + enable_irq(chip->irqs[BATT_MISSING_IRQ].irq); + enable_irq_wake(chip->irqs[BATT_MISSING_IRQ].irq); + } else { + disable_irq_wake(chip->irqs[BATT_MISSING_IRQ].irq); + disable_irq(chip->irqs[BATT_MISSING_IRQ].irq); + } + + return 0; +} + static int fg_delta_bsoc_irq_en_cb(struct votable *votable, void *data, int enable, const char *client) { @@ -1315,9 +1340,16 @@ static int fg_cap_learning_process_full_data(struct fg_chip *chip) return rc; } - cc_soc_delta_pct = DIV_ROUND_CLOSEST( - abs(cc_soc_sw - chip->cl.init_cc_soc_sw) * 100, - CC_SOC_30BIT); + cc_soc_delta_pct = + div64_s64((int64_t)(cc_soc_sw - chip->cl.init_cc_soc_sw) * 100, + CC_SOC_30BIT); + + /* If the delta is < 50%, then skip processing full data */ + if (cc_soc_delta_pct < 50) { + pr_err("cc_soc_delta_pct: %d\n", cc_soc_delta_pct); + return -ERANGE; + } + delta_cc_uah = div64_s64(chip->cl.learned_cc_uah * cc_soc_delta_pct, 100); chip->cl.final_cc_uah = chip->cl.init_cc_uah + delta_cc_uah; @@ -1387,10 +1419,10 @@ out: return rc; } -#define FULL_SOC_RAW 255 static void fg_cap_learning_update(struct fg_chip *chip) { int rc, batt_soc, batt_soc_msb; + bool input_present = is_input_present(chip); mutex_lock(&chip->cl.lock); @@ -1431,11 +1463,29 @@ static void fg_cap_learning_update(struct fg_chip *chip) chip->cl.init_cc_uah = 0; } + if (chip->charge_status == POWER_SUPPLY_STATUS_DISCHARGING) { + if (!input_present) { + fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n", + batt_soc_msb); + chip->cl.active = false; + chip->cl.init_cc_uah = 0; + } + } + if (chip->charge_status == POWER_SUPPLY_STATUS_NOT_CHARGING) { - fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n", - batt_soc_msb); - chip->cl.active = false; - chip->cl.init_cc_uah = 0; + if (is_qnovo_en(chip) && input_present) { + /* + * Don't abort the capacity learning when qnovo + * is enabled and input is present where the + * charging status can go to "not charging" + * intermittently. + */ + } else { + fg_dbg(chip, FG_CAP_LEARN, "Capacity learning aborted @ battery SOC %d\n", + batt_soc_msb); + chip->cl.active = false; + chip->cl.init_cc_uah = 0; + } } } @@ -1970,7 +2020,7 @@ static int fg_esr_fcc_config(struct fg_chip *chip) { union power_supply_propval prop = {0, }; int rc; - bool parallel_en = false, qnovo_en = false; + bool parallel_en = false, qnovo_en; if (is_parallel_charger_available(chip)) { rc = power_supply_get_property(chip->parallel_psy, @@ -1983,10 +2033,7 @@ static int fg_esr_fcc_config(struct fg_chip *chip) parallel_en = prop.intval; } - rc = power_supply_get_property(chip->batt_psy, - POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE, &prop); - if (!rc) - qnovo_en = prop.intval; + qnovo_en = is_qnovo_en(chip); fg_dbg(chip, FG_POWER_SUPPLY, "chg_sts: %d par_en: %d qnov_en: %d esr_fcc_ctrl_en: %d\n", chip->charge_status, parallel_en, qnovo_en, @@ -2039,6 +2086,50 @@ static int fg_esr_fcc_config(struct fg_chip *chip) return 0; } +static int fg_esr_timer_config(struct fg_chip *chip, bool sleep) +{ + int rc, cycles_init, cycles_max; + bool end_of_charge = false; + + end_of_charge = is_input_present(chip) && chip->charge_done; + fg_dbg(chip, FG_STATUS, "sleep: %d eoc: %d\n", sleep, end_of_charge); + + /* ESR discharging timer configuration */ + cycles_init = sleep ? chip->dt.esr_timer_asleep[TIMER_RETRY] : + chip->dt.esr_timer_awake[TIMER_RETRY]; + if (end_of_charge) + cycles_init = 0; + + cycles_max = sleep ? chip->dt.esr_timer_asleep[TIMER_MAX] : + chip->dt.esr_timer_awake[TIMER_MAX]; + + rc = fg_set_esr_timer(chip, cycles_init, cycles_max, false, + sleep ? FG_IMA_NO_WLOCK : FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in setting ESR timer, rc=%d\n", rc); + return rc; + } + + /* ESR charging timer configuration */ + cycles_init = cycles_max = -EINVAL; + if (end_of_charge || sleep) { + cycles_init = chip->dt.esr_timer_charging[TIMER_RETRY]; + cycles_max = chip->dt.esr_timer_charging[TIMER_MAX]; + } else if (is_input_present(chip)) { + cycles_init = chip->esr_timer_charging_default[TIMER_RETRY]; + cycles_max = chip->esr_timer_charging_default[TIMER_MAX]; + } + + rc = fg_set_esr_timer(chip, cycles_init, cycles_max, true, + sleep ? FG_IMA_NO_WLOCK : FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in setting ESR timer, rc=%d\n", rc); + return rc; + } + + return 0; +} + static void fg_batt_avg_update(struct fg_chip *chip) { if (chip->charge_status == chip->prev_charge_status) @@ -2112,6 +2203,10 @@ static void status_change_work(struct work_struct *work) if (rc < 0) pr_err("Error in adjusting FCC for ESR, rc=%d\n", rc); + rc = fg_esr_timer_config(chip, false); + if (rc < 0) + pr_err("Error in configuring ESR timer, rc=%d\n", rc); + rc = fg_get_battery_temp(chip, &batt_temp); if (!rc) { rc = fg_slope_limit_config(chip, batt_temp); @@ -2439,6 +2534,23 @@ static void profile_load_work(struct work_struct *work) int rc; vote(chip->awake_votable, PROFILE_LOAD, true, 0); + + rc = fg_get_batt_id(chip); + if (rc < 0) { + pr_err("Error in getting battery id, rc:%d\n", rc); + goto out; + } + + rc = fg_get_batt_profile(chip); + if (rc < 0) { + pr_warn("profile for batt_id=%dKOhms not found..using OTP, rc:%d\n", + chip->batt_id_ohms / 1000, rc); + goto out; + } + + if (!chip->profile_available) + goto out; + if (!is_profile_load_required(chip)) goto done; @@ -2503,9 +2615,9 @@ done: batt_psy_initialized(chip); fg_notify_charger(chip); chip->profile_loaded = true; - chip->soc_reporting_ready = true; fg_dbg(chip, FG_STATUS, "profile loaded successfully"); out: + chip->soc_reporting_ready = true; vote(chip->awake_votable, PROFILE_LOAD, false, 0); } @@ -3024,6 +3136,7 @@ static int fg_psy_set_property(struct power_supply *psy, pval->intval); return -EINVAL; } + break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: rc = fg_set_constant_chg_voltage(chip, pval->intval); break; @@ -3115,6 +3228,8 @@ static const struct power_supply_desc fg_psy_desc = { /* INIT FUNCTIONS STAY HERE */ +#define DEFAULT_ESR_CHG_TIMER_RETRY 8 +#define DEFAULT_ESR_CHG_TIMER_MAX 16 static int fg_hw_init(struct fg_chip *chip) { int rc; @@ -3283,22 +3398,29 @@ static int fg_hw_init(struct fg_chip *chip) return rc; } - if (chip->dt.esr_timer_charging > 0) { - rc = fg_set_esr_timer(chip, chip->dt.esr_timer_charging, true, - FG_IMA_DEFAULT); - if (rc < 0) { - pr_err("Error in setting ESR timer, rc=%d\n", rc); - return rc; - } + if (chip->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) { + chip->esr_timer_charging_default[TIMER_RETRY] = + DEFAULT_ESR_CHG_TIMER_RETRY; + chip->esr_timer_charging_default[TIMER_MAX] = + DEFAULT_ESR_CHG_TIMER_MAX; + } else { + /* We don't need this for pm660 at present */ + chip->esr_timer_charging_default[TIMER_RETRY] = -EINVAL; + chip->esr_timer_charging_default[TIMER_MAX] = -EINVAL; } - if (chip->dt.esr_timer_awake > 0) { - rc = fg_set_esr_timer(chip, chip->dt.esr_timer_awake, false, - FG_IMA_DEFAULT); - if (rc < 0) { - pr_err("Error in setting ESR timer, rc=%d\n", rc); - return rc; - } + rc = fg_set_esr_timer(chip, chip->dt.esr_timer_charging[TIMER_RETRY], + chip->dt.esr_timer_charging[TIMER_MAX], true, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in setting ESR timer, rc=%d\n", rc); + return rc; + } + + rc = fg_set_esr_timer(chip, chip->dt.esr_timer_awake[TIMER_RETRY], + chip->dt.esr_timer_awake[TIMER_MAX], false, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in setting ESR timer, rc=%d\n", rc); + return rc; } if (chip->cyc_ctr.en) @@ -3481,20 +3603,6 @@ static irqreturn_t fg_batt_missing_irq_handler(int irq, void *data) return IRQ_HANDLED; } - rc = fg_get_batt_id(chip); - if (rc < 0) { - chip->soc_reporting_ready = true; - pr_err("Error in getting battery id, rc:%d\n", rc); - return IRQ_HANDLED; - } - - rc = fg_get_batt_profile(chip); - if (rc < 0) { - chip->soc_reporting_ready = true; - pr_err("Error in getting battery profile, rc:%d\n", rc); - return IRQ_HANDLED; - } - clear_battery_profile(chip); schedule_delayed_work(&chip->profile_load_work, 0); @@ -3778,6 +3886,32 @@ static int fg_register_interrupts(struct fg_chip *chip) return 0; } +static int fg_parse_dt_property_u32_array(struct device_node *node, + const char *prop_name, int *buf, int len) +{ + int rc; + + rc = of_property_count_elems_of_size(node, prop_name, sizeof(u32)); + if (rc < 0) { + if (rc == -EINVAL) + return 0; + else + return rc; + } else if (rc != len) { + pr_err("Incorrect length %d for %s, rc=%d\n", len, prop_name, + rc); + return -EINVAL; + } + + rc = of_property_read_u32_array(node, prop_name, buf, len); + if (rc < 0) { + pr_err("Error in reading %s, rc=%d\n", prop_name, rc); + return rc; + } + + return 0; +} + static int fg_parse_slope_limit_coefficients(struct fg_chip *chip) { struct device_node *node = chip->dev->of_node; @@ -3788,17 +3922,10 @@ static int fg_parse_slope_limit_coefficients(struct fg_chip *chip) if (rc < 0) return 0; - rc = of_property_count_elems_of_size(node, "qcom,slope-limit-coeffs", - sizeof(u32)); - if (rc != SLOPE_LIMIT_NUM_COEFFS) - return -EINVAL; - - rc = of_property_read_u32_array(node, "qcom,slope-limit-coeffs", - chip->dt.slope_limit_coeffs, SLOPE_LIMIT_NUM_COEFFS); - if (rc < 0) { - pr_err("Error in reading qcom,slope-limit-coeffs, rc=%d\n", rc); + rc = fg_parse_dt_property_u32_array(node, "qcom,slope-limit-coeffs", + chip->dt.slope_limit_coeffs, SLOPE_LIMIT_NUM_COEFFS); + if (rc < 0) return rc; - } for (i = 0; i < SLOPE_LIMIT_NUM_COEFFS; i++) { if (chip->dt.slope_limit_coeffs[i] > SLOPE_LIMIT_COEFF_MAX || @@ -3817,44 +3944,20 @@ static int fg_parse_ki_coefficients(struct fg_chip *chip) struct device_node *node = chip->dev->of_node; int rc, i; - rc = of_property_count_elems_of_size(node, "qcom,ki-coeff-soc-dischg", - sizeof(u32)); - if (rc != KI_COEFF_SOC_LEVELS) - return 0; - - rc = of_property_read_u32_array(node, "qcom,ki-coeff-soc-dischg", - chip->dt.ki_coeff_soc, KI_COEFF_SOC_LEVELS); - if (rc < 0) { - pr_err("Error in reading ki-coeff-soc-dischg, rc=%d\n", - rc); + rc = fg_parse_dt_property_u32_array(node, "qcom,ki-coeff-soc-dischg", + chip->dt.ki_coeff_soc, KI_COEFF_SOC_LEVELS); + if (rc < 0) return rc; - } - - rc = of_property_count_elems_of_size(node, "qcom,ki-coeff-med-dischg", - sizeof(u32)); - if (rc != KI_COEFF_SOC_LEVELS) - return 0; - rc = of_property_read_u32_array(node, "qcom,ki-coeff-med-dischg", - chip->dt.ki_coeff_med_dischg, KI_COEFF_SOC_LEVELS); - if (rc < 0) { - pr_err("Error in reading ki-coeff-med-dischg, rc=%d\n", - rc); + rc = fg_parse_dt_property_u32_array(node, "qcom,ki-coeff-med-dischg", + chip->dt.ki_coeff_med_dischg, KI_COEFF_SOC_LEVELS); + if (rc < 0) return rc; - } - - rc = of_property_count_elems_of_size(node, "qcom,ki-coeff-hi-dischg", - sizeof(u32)); - if (rc != KI_COEFF_SOC_LEVELS) - return 0; - rc = of_property_read_u32_array(node, "qcom,ki-coeff-hi-dischg", - chip->dt.ki_coeff_hi_dischg, KI_COEFF_SOC_LEVELS); - if (rc < 0) { - pr_err("Error in reading ki-coeff-hi-dischg, rc=%d\n", - rc); + rc = fg_parse_dt_property_u32_array(node, "qcom,ki-coeff-hi-dischg", + chip->dt.ki_coeff_hi_dischg, KI_COEFF_SOC_LEVELS); + if (rc < 0) return rc; - } for (i = 0; i < KI_COEFF_SOC_LEVELS; i++) { if (chip->dt.ki_coeff_soc[i] < 0 || @@ -4099,23 +4202,26 @@ static int fg_parse_dt(struct fg_chip *chip) rc); } - rc = of_property_read_u32(node, "qcom,fg-esr-timer-charging", &temp); - if (rc < 0) - chip->dt.esr_timer_charging = -EINVAL; - else - chip->dt.esr_timer_charging = temp; + rc = fg_parse_dt_property_u32_array(node, "qcom,fg-esr-timer-charging", + chip->dt.esr_timer_charging, NUM_ESR_TIMERS); + if (rc < 0) { + chip->dt.esr_timer_charging[TIMER_RETRY] = -EINVAL; + chip->dt.esr_timer_charging[TIMER_MAX] = -EINVAL; + } - rc = of_property_read_u32(node, "qcom,fg-esr-timer-awake", &temp); - if (rc < 0) - chip->dt.esr_timer_awake = -EINVAL; - else - chip->dt.esr_timer_awake = temp; + rc = fg_parse_dt_property_u32_array(node, "qcom,fg-esr-timer-awake", + chip->dt.esr_timer_awake, NUM_ESR_TIMERS); + if (rc < 0) { + chip->dt.esr_timer_awake[TIMER_RETRY] = -EINVAL; + chip->dt.esr_timer_awake[TIMER_MAX] = -EINVAL; + } - rc = of_property_read_u32(node, "qcom,fg-esr-timer-asleep", &temp); - if (rc < 0) - chip->dt.esr_timer_asleep = -EINVAL; - else - chip->dt.esr_timer_asleep = temp; + rc = fg_parse_dt_property_u32_array(node, "qcom,fg-esr-timer-asleep", + chip->dt.esr_timer_asleep, NUM_ESR_TIMERS); + if (rc < 0) { + chip->dt.esr_timer_asleep[TIMER_RETRY] = -EINVAL; + chip->dt.esr_timer_asleep[TIMER_MAX] = -EINVAL; + } chip->cyc_ctr.en = of_property_read_bool(node, "qcom,cycle-counter-en"); if (chip->cyc_ctr.en) @@ -4263,6 +4369,9 @@ static void fg_cleanup(struct fg_chip *chip) if (chip->delta_bsoc_irq_en_votable) destroy_votable(chip->delta_bsoc_irq_en_votable); + if (chip->batt_miss_irq_en_votable) + destroy_votable(chip->batt_miss_irq_en_votable); + if (chip->batt_id_chan) iio_channel_release(chip->batt_id_chan); @@ -4320,6 +4429,7 @@ static int fg_gen3_probe(struct platform_device *pdev) chip); if (IS_ERR(chip->awake_votable)) { rc = PTR_ERR(chip->awake_votable); + chip->awake_votable = NULL; goto exit; } @@ -4328,6 +4438,16 @@ static int fg_gen3_probe(struct platform_device *pdev) fg_delta_bsoc_irq_en_cb, chip); if (IS_ERR(chip->delta_bsoc_irq_en_votable)) { rc = PTR_ERR(chip->delta_bsoc_irq_en_votable); + chip->delta_bsoc_irq_en_votable = NULL; + goto exit; + } + + chip->batt_miss_irq_en_votable = create_votable("FG_BATT_MISS_IRQ", + VOTE_SET_ANY, + fg_batt_miss_irq_en_cb, chip); + if (IS_ERR(chip->batt_miss_irq_en_votable)) { + rc = PTR_ERR(chip->batt_miss_irq_en_votable); + chip->batt_miss_irq_en_votable = NULL; goto exit; } @@ -4352,19 +4472,6 @@ static int fg_gen3_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&chip->batt_avg_work, batt_avg_work); INIT_DELAYED_WORK(&chip->sram_dump_work, sram_dump_work); - rc = fg_get_batt_id(chip); - if (rc < 0) { - pr_err("Error in getting battery id, rc:%d\n", rc); - goto exit; - } - - rc = fg_get_batt_profile(chip); - if (rc < 0) { - chip->soc_reporting_ready = true; - pr_warn("profile for batt_id=%dKOhms not found..using OTP, rc:%d\n", - chip->batt_id_ohms / 1000, rc); - } - rc = fg_memif_init(chip); if (rc < 0) { dev_err(chip->dev, "Error in initializing FG_MEMIF, rc:%d\n", @@ -4408,12 +4515,15 @@ static int fg_gen3_probe(struct platform_device *pdev) goto exit; } - /* Keep SOC_UPDATE irq disabled until we require it */ + /* Keep SOC_UPDATE_IRQ disabled until we require it */ if (fg_irqs[SOC_UPDATE_IRQ].irq) disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq); - /* Keep BSOC_DELTA_IRQ irq disabled until we require it */ - rerun_election(chip->delta_bsoc_irq_en_votable); + /* Keep BSOC_DELTA_IRQ disabled until we require it */ + vote(chip->delta_bsoc_irq_en_votable, DELTA_BSOC_IRQ_VOTER, false, 0); + + /* Keep BATT_MISSING_IRQ disabled until we require it */ + vote(chip->batt_miss_irq_en_votable, BATT_MISS_IRQ_VOTER, false, 0); rc = fg_debugfs_create(chip); if (rc < 0) { @@ -4438,8 +4548,7 @@ static int fg_gen3_probe(struct platform_device *pdev) } device_init_wakeup(chip->dev, true); - if (chip->profile_available) - schedule_delayed_work(&chip->profile_load_work, 0); + schedule_delayed_work(&chip->profile_load_work, 0); pr_debug("FG GEN3 driver probed successfully\n"); return 0; @@ -4453,15 +4562,9 @@ static int fg_gen3_suspend(struct device *dev) struct fg_chip *chip = dev_get_drvdata(dev); int rc; - if (chip->dt.esr_timer_awake > 0 && chip->dt.esr_timer_asleep > 0) { - rc = fg_set_esr_timer(chip, chip->dt.esr_timer_asleep, false, - FG_IMA_NO_WLOCK); - if (rc < 0) { - pr_err("Error in setting ESR timer during suspend, rc=%d\n", - rc); - return rc; - } - } + rc = fg_esr_timer_config(chip, true); + if (rc < 0) + pr_err("Error in configuring ESR timer, rc=%d\n", rc); cancel_delayed_work_sync(&chip->batt_avg_work); if (fg_sram_dump) @@ -4474,15 +4577,9 @@ static int fg_gen3_resume(struct device *dev) struct fg_chip *chip = dev_get_drvdata(dev); int rc; - if (chip->dt.esr_timer_awake > 0 && chip->dt.esr_timer_asleep > 0) { - rc = fg_set_esr_timer(chip, chip->dt.esr_timer_awake, false, - FG_IMA_DEFAULT); - if (rc < 0) { - pr_err("Error in setting ESR timer during resume, rc=%d\n", - rc); - return rc; - } - } + rc = fg_esr_timer_config(chip, false); + if (rc < 0) + pr_err("Error in configuring ESR timer, rc=%d\n", rc); fg_circ_buf_clr(&chip->ibatt_circ_buf); fg_circ_buf_clr(&chip->vbatt_circ_buf); diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index 803851d2b3ef..0908eea3b186 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -266,6 +266,10 @@ module_param_named( debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR ); +static int __weak_chg_icl_ua = 500000; +module_param_named( + weak_chg_icl_ua, __weak_chg_icl_ua, int, S_IRUSR | S_IWUSR); + #define MICRO_1P5A 1500000 #define MICRO_P1A 100000 #define OTG_DEFAULT_DEGLITCH_TIME_MS 50 @@ -461,6 +465,8 @@ static int smb2_usb_get_prop(struct power_supply *psy, val->intval = 0; else val->intval = 1; + if (chg->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN) + val->intval = 0; break; case POWER_SUPPLY_PROP_VOLTAGE_MIN: val->intval = chg->voltage_min_uv; @@ -1462,15 +1468,6 @@ static int smb2_configure_typec(struct smb_charger *chg) return rc; } - /* configure power role for dual-role */ - rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, - TYPEC_POWER_ROLE_CMD_MASK, 0); - if (rc < 0) { - dev_err(chg->dev, - "Couldn't configure power role for DRP rc=%d\n", rc); - return rc; - } - /* * disable Type-C factory mode and stay in Attached.SRC state when VCONN * over-current happens @@ -1848,6 +1845,16 @@ static int smb2_init_hw(struct smb2 *chip) static int smb2_post_init(struct smb2 *chip) { struct smb_charger *chg = &chip->chg; + int rc; + + /* configure power role for dual-role */ + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_POWER_ROLE_CMD_MASK, 0); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't configure power role for DRP rc=%d\n", rc); + return rc; + } rerun_election(chg->usb_irq_enable_votable); @@ -2109,7 +2116,7 @@ static struct smb_irq_info smb2_irqs[] = { [SWITCH_POWER_OK_IRQ] = { .name = "switcher-power-ok", .handler = smblib_handle_switcher_power_ok, - .storm_data = {true, 1000, 3}, + .storm_data = {true, 1000, 8}, }, }; @@ -2303,6 +2310,7 @@ static int smb2_probe(struct platform_device *pdev) chg->dev = &pdev->dev; chg->param = v1_params; chg->debug_mask = &__debug_mask; + chg->weak_chg_icl_ua = &__weak_chg_icl_ua; chg->mode = PARALLEL_MASTER; chg->irq_info = smb2_irqs; chg->name = "PMI"; @@ -2414,7 +2422,11 @@ static int smb2_probe(struct platform_device *pdev) goto cleanup; } - smb2_post_init(chip); + rc = smb2_post_init(chip); + if (rc < 0) { + pr_err("Failed in post init rc=%d\n", rc); + goto cleanup; + } smb2_create_debugfs(chip); diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 7848ca4396d9..16db425514c3 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -261,7 +261,7 @@ static const struct apsd_result const smblib_apsd_results[] = { [FLOAT] = { .name = "FLOAT", .bit = FLOAT_CHARGER_BIT, - .pst = POWER_SUPPLY_TYPE_USB_DCP + .pst = POWER_SUPPLY_TYPE_USB_FLOAT }, [HVDCP2] = { .name = "HVDCP2", @@ -630,8 +630,29 @@ int smblib_mapping_cc_delta_from_field_value(struct smb_chg_param *param, static void smblib_uusb_removal(struct smb_charger *chg) { int rc; + struct smb_irq_data *data; + struct storm_watch *wdata; cancel_delayed_work_sync(&chg->pl_enable_work); + + if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) { + smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n"); + rc = regulator_disable(chg->dpdm_reg); + if (rc < 0) + smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n", + rc); + } + + if (chg->wa_flags & BOOST_BACK_WA) { + data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data; + if (data) { + wdata = &data->storm_data; + update_storm_count(wdata, WEAK_CHG_STORM_COUNT); + vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0); + vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER, + false, 0); + } + } vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0); vote(chg->awake_votable, PL_DELAY_VOTER, false, 0); @@ -737,7 +758,7 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg) return 0; } -static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count) +static int smblib_get_hw_pulse_cnt(struct smb_charger *chg, int *count) { int rc; u8 val[2]; @@ -771,6 +792,24 @@ static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count) return 0; } +static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count) +{ + int rc; + + /* Use software based pulse count if HW INOV is disabled */ + if (get_effective_result(chg->hvdcp_hw_inov_dis_votable) > 0) { + *count = chg->pulse_cnt; + return 0; + } + + /* Use h/w pulse count if autonomous mode is enabled */ + rc = smblib_get_hw_pulse_cnt(chg, count); + if (rc < 0) + smblib_err(chg, "failed to read h/w pulse count rc=%d\n", rc); + + return rc; +} + #define USBIN_25MA 25000 #define USBIN_100MA 100000 #define USBIN_150MA 150000 @@ -1127,7 +1166,7 @@ static int smblib_hvdcp_hw_inov_dis_vote_callback(struct votable *votable, * the pulse count register get zeroed when autonomous mode is * disabled. Track that in variables before disabling */ - rc = smblib_get_pulse_cnt(chg, &chg->pulse_cnt); + rc = smblib_get_hw_pulse_cnt(chg, &chg->pulse_cnt); if (rc < 0) { pr_err("failed to read QC_PULSE_COUNT_STATUS_REG rc=%d\n", rc); @@ -2303,7 +2342,6 @@ int smblib_get_prop_input_voltage_settled(struct smb_charger *chg, { const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); int rc, pulses; - u8 stat; val->intval = MICRO_5V; if (apsd_result == NULL) { @@ -2313,13 +2351,12 @@ int smblib_get_prop_input_voltage_settled(struct smb_charger *chg, switch (apsd_result->pst) { case POWER_SUPPLY_TYPE_USB_HVDCP_3: - rc = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, &stat); + rc = smblib_get_pulse_cnt(chg, &pulses); if (rc < 0) { smblib_err(chg, "Couldn't read QC_PULSE_COUNT rc=%d\n", rc); return 0; } - pulses = (stat & QC_PULSE_COUNT_MASK); val->intval = MICRO_5V + HVDCP3_STEP_UV * pulses; break; default: @@ -2824,13 +2861,13 @@ int smblib_get_prop_fcc_delta(struct smb_charger *chg, * USB MAIN PSY SETTERS * *************************/ -#define SDP_CURRENT_MA 500000 -#define CDP_CURRENT_MA 1500000 -#define DCP_CURRENT_MA 1500000 -#define HVDCP_CURRENT_MA 3000000 -#define TYPEC_DEFAULT_CURRENT_MA 900000 -#define TYPEC_MEDIUM_CURRENT_MA 1500000 -#define TYPEC_HIGH_CURRENT_MA 3000000 +#define SDP_CURRENT_UA 500000 +#define CDP_CURRENT_UA 1500000 +#define DCP_CURRENT_UA 1500000 +#define HVDCP_CURRENT_UA 3000000 +#define TYPEC_DEFAULT_CURRENT_UA 900000 +#define TYPEC_MEDIUM_CURRENT_UA 1500000 +#define TYPEC_HIGH_CURRENT_UA 3000000 int smblib_get_charge_current(struct smb_charger *chg, int *total_current_ua) { @@ -2864,19 +2901,19 @@ int smblib_get_charge_current(struct smb_charger *chg, /* QC 2.0/3.0 adapter */ if (apsd_result->bit & (QC_3P0_BIT | QC_2P0_BIT)) { - *total_current_ua = HVDCP_CURRENT_MA; + *total_current_ua = HVDCP_CURRENT_UA; return 0; } if (non_compliant) { switch (apsd_result->bit) { case CDP_CHARGER_BIT: - current_ua = CDP_CURRENT_MA; + current_ua = CDP_CURRENT_UA; break; case DCP_CHARGER_BIT: case OCP_CHARGER_BIT: case FLOAT_CHARGER_BIT: - current_ua = DCP_CURRENT_MA; + current_ua = DCP_CURRENT_UA; break; default: current_ua = 0; @@ -2891,7 +2928,7 @@ int smblib_get_charge_current(struct smb_charger *chg, case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: switch (apsd_result->bit) { case CDP_CHARGER_BIT: - current_ua = CDP_CURRENT_MA; + current_ua = CDP_CURRENT_UA; break; case DCP_CHARGER_BIT: case OCP_CHARGER_BIT: @@ -2904,10 +2941,10 @@ int smblib_get_charge_current(struct smb_charger *chg, } break; case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: - current_ua = TYPEC_MEDIUM_CURRENT_MA; + current_ua = TYPEC_MEDIUM_CURRENT_UA; break; case POWER_SUPPLY_TYPEC_SOURCE_HIGH: - current_ua = TYPEC_HIGH_CURRENT_MA; + current_ua = TYPEC_HIGH_CURRENT_UA; break; case POWER_SUPPLY_TYPEC_NON_COMPLIANT: case POWER_SUPPLY_TYPEC_NONE: @@ -3111,6 +3148,8 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg) int rc; u8 stat; bool vbus_rising; + struct smb_irq_data *data; + struct storm_watch *wdata; rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); if (rc < 0) { @@ -3120,10 +3159,23 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg) vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); - if (vbus_rising) + if (vbus_rising) { smblib_cc2_sink_removal_exit(chg); - else + } else { smblib_cc2_sink_removal_enter(chg); + if (chg->wa_flags & BOOST_BACK_WA) { + data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data; + if (data) { + wdata = &data->storm_data; + update_storm_count(wdata, + WEAK_CHG_STORM_COUNT); + vote(chg->usb_icl_votable, BOOST_BACK_VOTER, + false, 0); + vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER, + false, 0); + } + } + } power_supply_changed(chg->usb_psy); smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n", @@ -3136,6 +3188,8 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) int rc; u8 stat; bool vbus_rising; + struct smb_irq_data *data; + struct storm_watch *wdata; rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); if (rc < 0) { @@ -3172,8 +3226,18 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) schedule_delayed_work(&chg->pl_enable_work, msecs_to_jiffies(PL_DELAY_MS)); } else { - if (chg->wa_flags & BOOST_BACK_WA) - vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0); + if (chg->wa_flags & BOOST_BACK_WA) { + data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data; + if (data) { + wdata = &data->storm_data; + update_storm_count(wdata, + WEAK_CHG_STORM_COUNT); + vote(chg->usb_icl_votable, BOOST_BACK_VOTER, + false, 0); + vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER, + false, 0); + } + } if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) { smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n"); @@ -3296,13 +3360,12 @@ static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg) } if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3) { - rc = smblib_read(chg, QC_PULSE_COUNT_STATUS_REG, &stat); + rc = smblib_get_pulse_cnt(chg, &pulses); if (rc < 0) { smblib_err(chg, "Couldn't read QC_PULSE_COUNT rc=%d\n", rc); return; } - pulses = (stat & QC_PULSE_COUNT_MASK); if (pulses < QC3_PULSES_FOR_6V) smblib_set_opt_freq_buck(chg, @@ -3403,8 +3466,29 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg, rising ? "rising" : "falling"); } +static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode) +{ + int rp_ua; + + switch (typec_mode) { + case POWER_SUPPLY_TYPEC_SOURCE_HIGH: + rp_ua = TYPEC_HIGH_CURRENT_UA; + break; + case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: + case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: + /* fall through */ + default: + rp_ua = DCP_CURRENT_UA; + } + + return rp_ua; +} + static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) { + int typec_mode; + int rp_ua; + /* while PD is active it should have complete ICL control */ if (chg->pd_active) return; @@ -3425,7 +3509,10 @@ static void smblib_force_legacy_icl(struct smb_charger *chg, int pst) vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000); break; case POWER_SUPPLY_TYPE_USB_DCP: - vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000); + case POWER_SUPPLY_TYPE_USB_FLOAT: + typec_mode = smblib_get_prop_typec_mode(chg); + rp_ua = get_rp_based_dcp_current(chg, typec_mode); + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, rp_ua); break; case POWER_SUPPLY_TYPE_USB_HVDCP: case POWER_SUPPLY_TYPE_USB_HVDCP_3: @@ -3553,9 +3640,30 @@ static void typec_sink_removal(struct smb_charger *chg) static void smblib_handle_typec_removal(struct smb_charger *chg) { int rc; + struct smb_irq_data *data; + struct storm_watch *wdata; chg->cc2_detach_wa_active = false; + if (chg->dpdm_reg && regulator_is_enabled(chg->dpdm_reg)) { + smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n"); + rc = regulator_disable(chg->dpdm_reg); + if (rc < 0) + smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n", + rc); + } + + if (chg->wa_flags & BOOST_BACK_WA) { + data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data; + if (data) { + wdata = &data->storm_data; + update_storm_count(wdata, WEAK_CHG_STORM_COUNT); + vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0); + vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER, + false, 0); + } + } + /* reset APSD voters */ vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0); vote(chg->apsd_disable_votable, PD_VOTER, false, 0); @@ -3600,6 +3708,11 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) chg->pd_hard_reset = 0; chg->typec_legacy_valid = false; + /* reset back to 120mS tCC debounce */ + rc = smblib_masked_write(chg, MISC_CFG_REG, TCC_DEBOUNCE_20MS_BIT, 0); + if (rc < 0) + smblib_err(chg, "Couldn't set 120mS tCC debounce rc=%d\n", rc); + /* enable APSD CC trigger for next insertion */ rc = smblib_masked_write(chg, TYPE_C_CFG_REG, APSD_START_ON_CC_BIT, APSD_START_ON_CC_BIT); @@ -3651,6 +3764,13 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) unlock: mutex_unlock(&chg->vconn_oc_lock); + /* clear exit sink based on cc */ + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + EXIT_SNK_BASED_ON_CC_BIT, 0); + if (rc < 0) + smblib_err(chg, "Couldn't clear exit_sink_based_on_cc rc=%d\n", + rc); + typec_sink_removal(chg); smblib_update_usb_type(chg); } @@ -3673,12 +3793,40 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg) typec_sink_removal(chg); } +static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode) +{ + int rp_ua; + const struct apsd_result *apsd = smblib_get_apsd_result(chg); + + if ((apsd->pst != POWER_SUPPLY_TYPE_USB_DCP) + && (apsd->pst != POWER_SUPPLY_TYPE_USB_FLOAT)) + return; + + /* + * handle Rp change for DCP/FLOAT/OCP. + * Update the current only if the Rp is different from + * the last Rp value. + */ + smblib_dbg(chg, PR_MISC, "CC change old_mode=%d new_mode=%d\n", + chg->typec_mode, typec_mode); + + rp_ua = get_rp_based_dcp_current(chg, typec_mode); + vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, rp_ua); +} + static void smblib_handle_typec_cc_state_change(struct smb_charger *chg) { + int typec_mode; + if (chg->pr_swap_in_progress) return; - chg->typec_mode = smblib_get_prop_typec_mode(chg); + typec_mode = smblib_get_prop_typec_mode(chg); + if (chg->typec_present && (typec_mode != chg->typec_mode)) + smblib_handle_rp_change(chg, typec_mode); + + chg->typec_mode = typec_mode; + if (!chg->typec_present && chg->typec_mode != POWER_SUPPLY_TYPEC_NONE) { chg->typec_present = true; smblib_dbg(chg, PR_MISC, "TypeC %s insertion\n", @@ -3764,10 +3912,23 @@ irqreturn_t smblib_handle_high_duty_cycle(int irq, void *data) return IRQ_HANDLED; } +static void smblib_bb_removal_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + bb_removal_work.work); + + vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0); + vote(chg->awake_votable, BOOST_BACK_VOTER, false, 0); +} + +#define BOOST_BACK_UNVOTE_DELAY_MS 750 +#define BOOST_BACK_STORM_COUNT 3 +#define WEAK_CHG_STORM_COUNT 8 irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; + struct storm_watch *wdata = &irq_data->storm_data; int rc, usb_icl; u8 stat; @@ -3789,8 +3950,32 @@ irqreturn_t smblib_handle_switcher_power_ok(int irq, void *data) return IRQ_HANDLED; if (is_storming(&irq_data->storm_data)) { - smblib_err(chg, "Reverse boost detected: voting 0mA to suspend input\n"); - vote(chg->usb_icl_votable, BOOST_BACK_VOTER, true, 0); + /* This could be a weak charger reduce ICL */ + if (!is_client_vote_enabled(chg->usb_icl_votable, + WEAK_CHARGER_VOTER)) { + smblib_err(chg, + "Weak charger detected: voting %dmA ICL\n", + *chg->weak_chg_icl_ua / 1000); + vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER, + true, *chg->weak_chg_icl_ua); + /* + * reset storm data and set the storm threshold + * to 3 for reverse boost detection. + */ + update_storm_count(wdata, BOOST_BACK_STORM_COUNT); + } else { + smblib_err(chg, + "Reverse boost detected: voting 0mA to suspend input\n"); + vote(chg->usb_icl_votable, BOOST_BACK_VOTER, true, 0); + vote(chg->awake_votable, BOOST_BACK_VOTER, true, 0); + /* + * Remove the boost-back vote after a delay, to avoid + * permanently suspending the input if the boost-back + * condition is unintentionally hit. + */ + schedule_delayed_work(&chg->bb_removal_work, + msecs_to_jiffies(BOOST_BACK_UNVOTE_DELAY_MS)); + } } return IRQ_HANDLED; @@ -3824,12 +4009,18 @@ int smblib_get_prop_pr_swap_in_progress(struct smb_charger *chg, int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg, const union power_supply_propval *val) { + int rc; + chg->pr_swap_in_progress = val->intval; /* * call the cc changed irq to handle real removals while * PR_SWAP was in progress */ smblib_usb_typec_change(chg); + rc = smblib_masked_write(chg, MISC_CFG_REG, TCC_DEBOUNCE_20MS_BIT, + val->intval ? TCC_DEBOUNCE_20MS_BIT : 0); + if (rc < 0) + smblib_err(chg, "Couldn't set tCC debounce rc=%d\n", rc); return 0; } @@ -4228,6 +4419,7 @@ static void smblib_legacy_detection_work(struct work_struct *work) unlock: chg->typec_en_dis_active = 0; + smblib_usb_typec_change(chg); mutex_unlock(&chg->lock); } @@ -4438,6 +4630,7 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work); INIT_DELAYED_WORK(&chg->uusb_otg_work, smblib_uusb_otg_work); + INIT_DELAYED_WORK(&chg->bb_removal_work, smblib_bb_removal_work); chg->fake_capacity = -EINVAL; chg->fake_input_current_limited = -EINVAL; @@ -4493,6 +4686,7 @@ int smblib_deinit(struct smb_charger *chg) cancel_delayed_work_sync(&chg->pl_enable_work); cancel_work_sync(&chg->legacy_detection_work); cancel_delayed_work_sync(&chg->uusb_otg_work); + cancel_delayed_work_sync(&chg->bb_removal_work); power_supply_unreg_notifier(&chg->nb); smblib_destroy_votables(chg); qcom_batt_deinit(); diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index 8ba0a1dfe19f..c97b84c34084 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -64,9 +64,12 @@ enum print_reason { #define BATT_PROFILE_VOTER "BATT_PROFILE_VOTER" #define OTG_DELAY_VOTER "OTG_DELAY_VOTER" #define USBIN_I_VOTER "USBIN_I_VOTER" +#define WEAK_CHARGER_VOTER "WEAK_CHARGER_VOTER" #define VCONN_MAX_ATTEMPTS 3 #define OTG_MAX_ATTEMPTS 3 +#define BOOST_BACK_STORM_COUNT 3 +#define WEAK_CHG_STORM_COUNT 8 enum smb_mode { PARALLEL_MASTER = 0, @@ -230,6 +233,7 @@ struct smb_charger { struct smb_chg_freq chg_freq; int smb_version; int otg_delay_ms; + int *weak_chg_icl_ua; /* locks */ struct mutex lock; @@ -292,6 +296,7 @@ struct smb_charger { struct delayed_work pl_enable_work; struct work_struct legacy_detection_work; struct delayed_work uusb_otg_work; + struct delayed_work bb_removal_work; /* cached status */ int voltage_min_uv; diff --git a/drivers/power/supply/qcom/smb1351-charger.c b/drivers/power/supply/qcom/smb1351-charger.c index 7014fd706d9e..f5c8252b5e41 100644 --- a/drivers/power/supply/qcom/smb1351-charger.c +++ b/drivers/power/supply/qcom/smb1351-charger.c @@ -1416,6 +1416,7 @@ static enum power_supply_property smb1351_parallel_properties[] = { POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_PARALLEL_MODE, + POWER_SUPPLY_PROP_INPUT_SUSPEND, }; static int smb1351_parallel_set_chg_suspend(struct smb1351_charger *chip, @@ -1702,6 +1703,9 @@ static int smb1351_parallel_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_PARALLEL_MODE: val->intval = chip->parallel_mode; break; + case POWER_SUPPLY_PROP_INPUT_SUSPEND: + val->intval = chip->parallel_charger_suspended; + break; default: return -EINVAL; } diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c index 0ee53b100ae2..335b160e24a5 100644 --- a/drivers/power/supply/qcom/smb138x-charger.c +++ b/drivers/power/supply/qcom/smb138x-charger.c @@ -941,13 +941,6 @@ static int smb138x_init_slave_hw(struct smb138x *chip) return rc; } - rc = smblib_write(chg, THERMREG_SRC_CFG_REG, - THERMREG_SKIN_ADC_SRC_EN_BIT); - if (rc < 0) { - pr_err("Couldn't enable connector thermreg source rc=%d\n", rc); - return rc; - } - return 0; } diff --git a/drivers/power/supply/qcom/storm-watch.c b/drivers/power/supply/qcom/storm-watch.c index 5275079c53e0..21ac669f2ec9 100644 --- a/drivers/power/supply/qcom/storm-watch.c +++ b/drivers/power/supply/qcom/storm-watch.c @@ -64,3 +64,13 @@ void reset_storm_count(struct storm_watch *data) data->storm_count = 0; mutex_unlock(&data->storm_lock); } + +void update_storm_count(struct storm_watch *data, int max_count) +{ + if (!data) + return; + + mutex_lock(&data->storm_lock); + data->max_storm_count = max_count; + mutex_unlock(&data->storm_lock); +} diff --git a/drivers/power/supply/qcom/storm-watch.h b/drivers/power/supply/qcom/storm-watch.h index ff05c4a661c3..5275d73613d4 100644 --- a/drivers/power/supply/qcom/storm-watch.h +++ b/drivers/power/supply/qcom/storm-watch.h @@ -37,4 +37,5 @@ struct storm_watch { bool is_storming(struct storm_watch *data); void reset_storm_count(struct storm_watch *data); +void update_storm_count(struct storm_watch *data, int max_count); #endif diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 88956d3ba674..068c7ddfb739 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -344,6 +344,15 @@ config REGULATOR_MAX1586 regulator via I2C bus. The provided regulator is suitable for PXA27x chips to control VCC_CORE and VCC_USIM voltages. +config REGULATOR_MAX20010 + tristate "Maxim MAX20010 regulator support" + depends on I2C + help + This driver supports the Maxim MAX20010 switching voltage regulator + (buck converter). The regulator is controlled using an I2C interface + and supports 2 programmable voltage ranges from 0.5V to 1.27V in 10mV + steps and 0.625V to 1.5875V in 12.5mV steps. + config REGULATOR_MAX8649 tristate "Maxim 8649 voltage regulator" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index e345f10f94af..856ebe2b5c1b 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o obj-$(CONFIG_REGULATOR_MAX14577) += max14577.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o +obj-$(CONFIG_REGULATOR_MAX20010) += max20010-regulator.o obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o obj-$(CONFIG_REGULATOR_MAX8907) += max8907-regulator.o diff --git a/drivers/regulator/cpr3-hmss-regulator.c b/drivers/regulator/cpr3-hmss-regulator.c index 9a20b6925877..21381d1ad7fa 100644 --- a/drivers/regulator/cpr3-hmss-regulator.c +++ b/drivers/regulator/cpr3-hmss-regulator.c @@ -1505,7 +1505,7 @@ static int cpr3_hmss_init_regulator(struct cpr3_regulator *vreg) static int cpr3_hmss_init_aging(struct cpr3_controller *ctrl) { struct cpr3_msm8996_hmss_fuses *fuse = NULL; - struct cpr3_regulator *vreg; + struct cpr3_regulator *vreg = NULL; u32 aging_ro_scale; int i, j, rc; @@ -1710,7 +1710,7 @@ static int cpr3_hmss_regulator_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; const struct of_device_id *match; struct cpr3_controller *ctrl; - struct cpr3_regulator *vreg; + struct cpr3_regulator *vreg = NULL; int i, j, rc; if (!dev->of_node) { diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c index d131a8ea4144..3adbd41ede88 100644 --- a/drivers/regulator/cpr3-regulator.c +++ b/drivers/regulator/cpr3-regulator.c @@ -3117,7 +3117,8 @@ static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl) struct cpr4_sdelta *sdelta; bool valid = false; bool thread_valid; - int i, j, rc, new_volt, vdd_volt, dynamic_floor_volt, last_corner_volt; + int i, j, rc; + int new_volt, vdd_volt, dynamic_floor_volt, last_corner_volt = 0; u32 reg_last_measurement = 0, sdelta_size; int *sdelta_table, *boost_table; diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c index 2307da9497dc..ecf7885a4bff 100644 --- a/drivers/regulator/cprh-kbss-regulator.c +++ b/drivers/regulator/cprh-kbss-regulator.c @@ -1975,7 +1975,7 @@ static int cprh_kbss_init_regulator(struct cpr3_regulator *vreg) static int cprh_kbss_init_aging(struct cpr3_controller *ctrl) { struct cprh_kbss_fuses *fuse = NULL; - struct cpr3_regulator *vreg; + struct cpr3_regulator *vreg = NULL; u32 aging_ro_scale; int i, j, rc = 0; diff --git a/drivers/regulator/max20010-regulator.c b/drivers/regulator/max20010-regulator.c new file mode 100644 index 000000000000..a914ca70ccb7 --- /dev/null +++ b/drivers/regulator/max20010-regulator.c @@ -0,0 +1,490 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#include <linux/module.h> +#include <linux/param.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> +#include <linux/of_device.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/regmap.h> + +struct voltage_range { + int vrange_sel; + int min_uV; + int max_uV; + int step_uV; +}; + +struct max20010_slew_rate { + int slew_sel; + int soft_start; + int dvs; +}; + +struct max20010_device_info { + struct device *dev; + struct regulator_dev *rdev; + struct regulator_init_data *init_data; + struct regmap *regmap; + const struct voltage_range *range; + const struct max20010_slew_rate *slew_rate; + unsigned vout_sel; + bool enabled; +}; + +#define MAX20010_ID_REG 0x00 + +#define MAX20010_VMAX_REG 0x02 +#define MAX20010_VMAX_MASK GENMASK(6, 0) + +#define MAX20010_CONFIG_REG 0x05 +#define MAX20010_CONFIG_SYNC_IO_MASK GENMASK(1, 0) +#define MAX20010_CONFIG_MODE_MASK BIT(3) +#define MAX20010_CONFIG_MODE_SYNC 0 +#define MAX20010_CONFIG_MODE_FPWM 8 +#define MAX20010_CONFIG_VSTEP_MASK BIT(7) +#define MAX20010_CONFIG_VSTEP_SHIFT 7 + +#define MAX20010_SLEW_REG 0x06 +#define MAX20010_SLEW_MASK GENMASK(3, 0) + +#define MAX20010_VSET_REG 0x07 +#define MAX20010_VSET_MASK GENMASK(6, 0) + +static const struct max20010_slew_rate slew_rates[] = { + {0, 22000, 22000}, + {1, 11000, 22000}, + {2, 5500, 22000}, + {3, 11000, 11000}, + {4, 5500, 11000}, + {5, 44000, 44000}, + {6, 22000, 44000}, + {7, 11000, 44000}, + {8, 5500, 44000}, + {9, 5500, 5500}, +}; + +static const struct voltage_range max20010_range0 = {0, 500000, 1270000, 10000}; +static const struct voltage_range max20010_range1 = {1, 625000, 1587500, 12500}; + +static const struct regmap_config max20010_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX20010_VSET_REG, +}; + +static int max20010_set_voltage_sel(struct regulator_dev *rdev, unsigned sel) +{ + struct max20010_device_info *info = rdev_get_drvdata(rdev); + int rc = 0; + + /* Set the voltage only if the regulator was enabled earlier */ + if (info->enabled) { + rc = regulator_set_voltage_sel_regmap(rdev, sel); + if (rc) { + dev_err(info->dev, + "regulator set voltage failed for selector = 0x%2x, rc=%d\n", + sel, rc); + return rc; + } + } + + info->vout_sel = sel; + return rc; +} + +static int max20010_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct max20010_device_info *info = rdev_get_drvdata(rdev); + + return (info->enabled == true) ? 1 : 0; +} + +static int max20010_regulator_enable(struct regulator_dev *rdev) +{ + struct max20010_device_info *info = rdev_get_drvdata(rdev); + int rc = 0; + + rc = regulator_set_voltage_sel_regmap(rdev, info->vout_sel); + if (rc) { + dev_err(info->dev, "regulator enable failed, rc=%d\n", rc); + return rc; + } + info->enabled = true; + + return rc; +} + +static int max20010_regulator_disable(struct regulator_dev *rdev) +{ + struct max20010_device_info *info = rdev_get_drvdata(rdev); + int rc = 0; + + rc = regulator_set_voltage_sel_regmap(rdev, 0x0); + if (rc) { + dev_err(info->dev, "regulator disable failed, rc=%d\n", rc); + return rc; + } + info->enabled = false; + + return rc; +} + +static inline unsigned int max20010_map_mode(unsigned int mode) +{ + return (mode == MAX20010_CONFIG_MODE_FPWM) ? + REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE; +} + +static int max20010_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct max20010_device_info *info = rdev_get_drvdata(rdev); + int rc = 0; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + rc = regmap_update_bits(info->regmap, MAX20010_CONFIG_REG, + MAX20010_CONFIG_MODE_MASK, + MAX20010_CONFIG_MODE_FPWM); + break; + case REGULATOR_MODE_IDLE: + rc = regmap_update_bits(info->regmap, MAX20010_CONFIG_REG, + (MAX20010_CONFIG_MODE_MASK + | MAX20010_CONFIG_SYNC_IO_MASK), + MAX20010_CONFIG_MODE_SYNC); + break; + default: + return -EINVAL; + } + + if (rc) + dev_err(info->dev, "failed to set %s mode, rc=%d\n", + mode == REGULATOR_MODE_NORMAL ? "Force PWM" : "SYNC", + rc); + return rc; +} + +static unsigned int max20010_get_mode(struct regulator_dev *rdev) +{ + struct max20010_device_info *info = rdev_get_drvdata(rdev); + unsigned int val; + int rc = 0; + + rc = regmap_read(info->regmap, MAX20010_CONFIG_REG, &val); + if (rc) { + dev_err(info->dev, "failed to read mode configuration, rc=%d\n", + rc); + return rc; + } + + return max20010_map_mode(val & MAX20010_CONFIG_MODE_MASK); +} + +static int max20010_enable_time(struct regulator_dev *rdev) +{ + struct max20010_device_info *info = rdev_get_drvdata(rdev); + int volt_uV; + + volt_uV = regulator_list_voltage_linear(rdev, info->vout_sel); + return DIV_ROUND_UP(volt_uV, info->slew_rate->soft_start); +} + +static struct regulator_ops max20010_regulator_ops = { + .set_voltage_sel = max20010_set_voltage_sel, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .map_voltage = regulator_map_voltage_linear, + .list_voltage = regulator_list_voltage_linear, + .is_enabled = max20010_regulator_is_enabled, + .enable = max20010_regulator_enable, + .disable = max20010_regulator_disable, + .set_mode = max20010_set_mode, + .get_mode = max20010_get_mode, + .enable_time = max20010_enable_time, +}; + +static struct regulator_desc rdesc = { + .name = "max20010-reg", + .supply_name = "vin", + .owner = THIS_MODULE, + .ops = &max20010_regulator_ops, + .type = REGULATOR_VOLTAGE, + .linear_min_sel = 1, + .vsel_reg = MAX20010_VSET_REG, + .vsel_mask = MAX20010_VSET_MASK, + .of_map_mode = max20010_map_mode, +}; + +static int max20010_device_setup(struct max20010_device_info *info) +{ + int max_uV, rc = 0; + unsigned int val; + + rc = regmap_update_bits(info->regmap, MAX20010_CONFIG_REG, + MAX20010_CONFIG_VSTEP_MASK, + (info->range->vrange_sel + << MAX20010_CONFIG_VSTEP_SHIFT)); + if (rc) { + dev_err(info->dev, "failed to update vstep configuration, rc=%d\n", + rc); + return rc; + } + + max_uV = min(info->init_data->constraints.max_uV, info->range->max_uV); + val = DIV_ROUND_UP(max_uV - info->range->min_uV, + info->range->step_uV) + 1; + rc = regmap_update_bits(info->regmap, MAX20010_VMAX_REG, + MAX20010_VMAX_MASK, val); + if (rc) { + dev_err(info->dev, "failed to write VMAX configuration, rc=%d\n", + rc); + return rc; + } + + rc = regmap_update_bits(info->regmap, MAX20010_SLEW_REG, + MAX20010_SLEW_MASK, info->slew_rate->slew_sel); + if (rc) { + dev_err(info->dev, "failed to write slew configuration, rc=%d\n", + rc); + return rc; + } + + /* Store default voltage register value */ + rc = regmap_read(info->regmap, MAX20010_VSET_REG, &val); + if (rc) { + dev_err(info->dev, "failed to read voltage register, rc=%d\n", + rc); + return rc; + } + + info->vout_sel = val & MAX20010_VSET_MASK; + info->enabled = (info->vout_sel != 0x0) ? true : false; + + return rc; +} + +static int max20010_parse_init_data(struct max20010_device_info *info) +{ + struct device_node *of_node = info->dev->of_node; + int i, slew_index, ss_slew_rate, dvs_slew_rate, rc = 0; + unsigned int val; + + if (of_find_property(of_node, "maxim,vrange-sel", NULL)) { + rc = of_property_read_u32(of_node, "maxim,vrange-sel", &val); + if (rc) { + dev_err(info->dev, "maxim,vrange-sel property read failed, rc=%d\n", + rc); + return rc; + } else if (val > 1) { + dev_err(info->dev, "unsupported vrange-sel value = %d, should be either 0 or 1\n", + val); + return -EINVAL; + } + } else { + /* Read default voltage range value */ + rc = regmap_read(info->regmap, MAX20010_CONFIG_REG, &val); + if (rc) { + dev_err(info->dev, "failed to read config register, rc=%d\n", + rc); + return rc; + } + + val = (val & MAX20010_CONFIG_VSTEP_MASK) + >> MAX20010_CONFIG_VSTEP_SHIFT; + } + + info->range = (val == 0) ? &max20010_range0 : &max20010_range1; + + /* + * Verify the min and max constraints specified through regulator device + * properties are fit with in that of the selected voltage range of the + * device. + */ + if (info->init_data->constraints.min_uV < info->range->min_uV || + info->init_data->constraints.max_uV > info->range->max_uV) { + dev_err(info->dev, + "Regulator min/max constraints are not fit with in the device min/max constraints\n"); + return -EINVAL; + } + + /* + * Read soft-start and dvs slew rates from device node. Use default + * values if not specified. + * + * Read the register default values and modify them with the slew-rates + * defined through device node. + */ + rc = regmap_read(info->regmap, MAX20010_SLEW_REG, &val); + if (rc) { + dev_err(info->dev, "failed to read slew register, rc=%d\n", + rc); + return rc; + } + + slew_index = val & MAX20010_SLEW_MASK; + + if (slew_index >= ARRAY_SIZE(slew_rates)) { + dev_err(info->dev, "unsupported default slew configuration\n"); + return -EINVAL; + } + + ss_slew_rate = slew_rates[slew_index].soft_start; + dvs_slew_rate = slew_rates[slew_index].dvs; + + if (of_find_property(of_node, "maxim,soft-start-slew-rate", NULL)) { + rc = of_property_read_u32(of_node, "maxim,soft-start-slew-rate", + &val); + if (rc) { + dev_err(info->dev, "maxim,soft-start-slew-rate read failed, rc=%d\n", + rc); + return rc; + } + + ss_slew_rate = val; + } + + if (of_find_property(of_node, "maxim,dvs-slew-rate", NULL)) { + rc = of_property_read_u32(of_node, "maxim,dvs-slew-rate", + &val); + if (rc) { + dev_err(info->dev, "maxim,dvs-slew-rate read failed, rc=%d\n", + rc); + return rc; + } + + dvs_slew_rate = val; + } + + for (i = 0; i < ARRAY_SIZE(slew_rates); i++) { + if (ss_slew_rate == slew_rates[i].soft_start + && dvs_slew_rate == slew_rates[i].dvs) { + info->slew_rate = &slew_rates[i]; + break; + } + } + + if (i == ARRAY_SIZE(slew_rates)) { + dev_err(info->dev, "invalid slew-rate values are specified.\n"); + return -EINVAL; + } + + return rc; +} + +static int max20010_regulator_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct max20010_device_info *info; + struct regulator_config config = { }; + int val, rc = 0; + + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->dev = &client->dev; + info->init_data = of_get_regulator_init_data(info->dev, + info->dev->of_node, &rdesc); + if (!info->init_data) { + dev_err(info->dev, "regulator init_data is missing\n"); + return -ENODEV; + } + + info->init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE; + info->init_data->constraints.valid_modes_mask + = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE; + + info->regmap = devm_regmap_init_i2c(client, &max20010_regmap_config); + if (IS_ERR(info->regmap)) { + dev_err(info->dev, "Error in allocating regmap\n"); + return PTR_ERR(info->regmap); + } + + i2c_set_clientdata(client, info); + + /* Get chip Id */ + rc = regmap_read(info->regmap, MAX20010_ID_REG, &val); + if (rc) { + dev_err(info->dev, "Failed to get chip ID!\n"); + return rc; + } + + rc = max20010_parse_init_data(info); + if (rc) { + dev_err(info->dev, "max20010 init data parsing failed, rc=%d\n", + rc); + return rc; + } + + rc = max20010_device_setup(info); + if (rc) { + dev_err(info->dev, "Failed to setup device, rc=%d\n", + rc); + return rc; + } + + config.dev = info->dev; + config.init_data = info->init_data; + config.regmap = info->regmap; + config.driver_data = info; + config.of_node = client->dev.of_node; + + rdesc.min_uV = info->range->min_uV; + rdesc.uV_step = info->range->step_uV; + rdesc.n_voltages = DIV_ROUND_UP((info->range->max_uV + - info->range->min_uV), + info->range->step_uV); + rdesc.ramp_delay = info->slew_rate->dvs; + + info->rdev = devm_regulator_register(info->dev, &rdesc, &config); + if (IS_ERR(info->rdev)) { + dev_err(info->dev, "Failed to register regulator, rc=%d\n", rc); + return PTR_ERR(info->rdev); + } + + dev_info(info->dev, "Detected regulator MAX20010 PID = %d : voltage-range(%d) : (%d - %d) uV, step = %d uV\n", + val, info->range->vrange_sel, info->range->min_uV, + info->range->max_uV, info->range->step_uV); + + return rc; +} + +static const struct of_device_id max20010_match_table[] = { + {.compatible = "maxim,max20010", }, + { }, +}; +MODULE_DEVICE_TABLE(of, max20010_match_table); + +static const struct i2c_device_id max20010_id[] = { + {"max20010", -1}, + { }, +}; +MODULE_DEVICE_TABLE(i2c, max20010_id); + +static struct i2c_driver max20010_regulator_driver = { + .driver = { + .name = "max20010-regulator", + .owner = THIS_MODULE, + .of_match_table = max20010_match_table, + }, + .probe = max20010_regulator_probe, + .id_table = max20010_id, +}; +module_i2c_driver(max20010_regulator_driver); + +MODULE_DESCRIPTION("MAX20010 regulator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/qpnp-regulator.c b/drivers/regulator/qpnp-regulator.c index bd706658348d..08e991fa7db3 100644 --- a/drivers/regulator/qpnp-regulator.c +++ b/drivers/regulator/qpnp-regulator.c @@ -127,6 +127,9 @@ enum qpnp_common_regulator_registers { QPNP_COMMON_REG_ENABLE = 0x46, QPNP_COMMON_REG_PULL_DOWN = 0x48, QPNP_COMMON_REG_STEP_CTRL = 0x61, + QPNP_COMMON_REG_UL_LL_CTRL = 0x68, + QPNP_COMMON_REG_VOLTAGE_ULS_VALID = 0x6A, + QPNP_COMMON_REG_VOLTAGE_LLS_VALID = 0x6C, }; /* @@ -139,6 +142,8 @@ enum qpnp_common2_regulator_registers { QPNP_COMMON2_REG_VOLTAGE_MSB = 0x41, QPNP_COMMON2_REG_MODE = 0x45, QPNP_COMMON2_REG_STEP_CTRL = 0x61, + QPNP_COMMON2_REG_VOLTAGE_ULS_LSB = 0x68, + QPNP_COMMON2_REG_VOLTAGE_ULS_MSB = 0x69, }; enum qpnp_ldo_registers { @@ -205,6 +210,10 @@ enum qpnp_common2_control_register_index { /* Common regulator pull down control register layout */ #define QPNP_COMMON_PULL_DOWN_ENABLE_MASK 0x80 +/* Common regulator UL & LL limits control register layout */ +#define QPNP_COMMON_UL_EN_MASK 0x80 +#define QPNP_COMMON_LL_EN_MASK 0x40 + /* LDO regulator current limit control register layout */ #define QPNP_LDO_CURRENT_LIMIT_ENABLE_MASK 0x80 @@ -1749,6 +1758,89 @@ static int qpnp_regulator_match(struct qpnp_regulator *vreg) return rc; } +static int qpnp_regulator_check_constraints(struct qpnp_regulator *vreg, + struct qpnp_regulator_platform_data *pdata) +{ + struct qpnp_voltage_range *range = NULL; + int i, rc = 0, limit_min_uV, limit_max_uV, max_uV; + u8 reg[2]; + + limit_min_uV = 0; + limit_max_uV = INT_MAX; + + if (vreg->logical_type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS) { + max_uV = pdata->init_data.constraints.max_uV; + /* Find the range which max_uV is inside of. */ + for (i = vreg->set_points->count - 1; i > 0; i--) { + range = &vreg->set_points->range[i]; + if (range->set_point_max_uV > 0 + && max_uV >= range->set_point_min_uV + && max_uV <= range->set_point_max_uV) + break; + } + + if (i < 0 || range == NULL) { + vreg_err(vreg, "max_uV doesn't fit in any voltage range\n"); + return -EINVAL; + } + + rc = qpnp_vreg_read(vreg, QPNP_COMMON_REG_UL_LL_CTRL, + ®[0], 1); + if (rc) { + vreg_err(vreg, "UL_LL register read failed, rc=%d\n", + rc); + return rc; + } + + if (reg[0] & QPNP_COMMON_UL_EN_MASK) { + rc = qpnp_vreg_read(vreg, + QPNP_COMMON_REG_VOLTAGE_ULS_VALID, + ®[1], 1); + if (rc) { + vreg_err(vreg, "ULS_VALID register read failed, rc=%d\n", + rc); + return rc; + } + + limit_max_uV = range->step_uV * reg[1] + range->min_uV; + } + + if (reg[0] & QPNP_COMMON_LL_EN_MASK) { + rc = qpnp_vreg_read(vreg, + QPNP_COMMON_REG_VOLTAGE_LLS_VALID, + ®[1], 1); + if (rc) { + vreg_err(vreg, "LLS_VALID register read failed, rc=%d\n", + rc); + return rc; + } + + limit_min_uV = range->step_uV * reg[1] + range->min_uV; + } + } else if (vreg->logical_type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS2) { + rc = qpnp_vreg_read(vreg, QPNP_COMMON2_REG_VOLTAGE_ULS_LSB, + reg, 2); + if (rc) { + vreg_err(vreg, "ULS registers read failed, rc=%d\n", + rc); + return rc; + } + + limit_max_uV = (((int)reg[1] << 8) | (int)reg[0]) * 1000; + } + + if (pdata->init_data.constraints.min_uV < limit_min_uV + || pdata->init_data.constraints.max_uV > limit_max_uV) { + vreg_err(vreg, "regulator min/max(%d/%d) constraints do not fit within HW configured min/max(%d/%d) constraints\n", + pdata->init_data.constraints.min_uV, + pdata->init_data.constraints.max_uV, + limit_min_uV, limit_max_uV); + return -EINVAL; + } + + return 0; +} + static int qpnp_regulator_ftsmps_init_slew_rate(struct qpnp_regulator *vreg) { int rc; @@ -2282,6 +2374,13 @@ static int qpnp_regulator_probe(struct platform_device *pdev) } } + rc = qpnp_regulator_check_constraints(vreg, pdata); + if (rc) { + vreg_err(vreg, "regulator constraints check failed, rc=%d\n", + rc); + goto bail; + } + rc = qpnp_regulator_init_registers(vreg, pdata); if (rc) { vreg_err(vreg, "common initialization failed, rc=%d\n", rc); diff --git a/drivers/regulator/rpm-smd-regulator.c b/drivers/regulator/rpm-smd-regulator.c index d26fd3bea788..a5dc1b983bcb 100644 --- a/drivers/regulator/rpm-smd-regulator.c +++ b/drivers/regulator/rpm-smd-regulator.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -207,6 +207,7 @@ struct rpm_regulator { bool use_pin_ctrl_for_enable; struct rpm_vreg_request req; int system_load; + int hpm_threshold_current; int min_uV; int max_uV; u32 pin_ctrl_mask[RPM_VREG_PIN_CTRL_STATE_COUNT]; @@ -1030,6 +1031,34 @@ static unsigned int rpm_vreg_get_bob_mode(struct regulator_dev *rdev) return mode; } +static unsigned int rpm_vreg_get_optimum_mode(struct regulator_dev *rdev, + int input_uV, int output_uV, int load_uA) +{ + struct rpm_regulator *reg = rdev_get_drvdata(rdev); + u32 mode = REGULATOR_MODE_NORMAL; + + if (reg->hpm_threshold_current > 0) { + if (load_uA >= reg->hpm_threshold_current) { + /* PWM mode */ + mode = (reg->rpm_vreg->regulator_type + == RPM_REGULATOR_TYPE_BOB) + ? REGULATOR_MODE_FAST + : REGULATOR_MODE_NORMAL; + } else { + /* AUTO mode */ + mode = (reg->rpm_vreg->regulator_type + == RPM_REGULATOR_TYPE_BOB) + ? REGULATOR_MODE_NORMAL + : REGULATOR_MODE_IDLE; + } + } else { + /* Default to the current mode if no threshold is present. */ + mode = reg->rdesc.ops->get_mode(rdev); + } + + return mode; +} + static int rpm_vreg_enable_time(struct regulator_dev *rdev) { struct rpm_regulator *reg = rdev_get_drvdata(rdev); @@ -1402,6 +1431,18 @@ static struct regulator_ops smps_ops = { .enable_time = rpm_vreg_enable_time, }; +static struct regulator_ops smps_optimum_mode_ops = { + .enable = rpm_vreg_enable, + .disable = rpm_vreg_disable, + .is_enabled = rpm_vreg_is_enabled, + .set_voltage = rpm_vreg_set_voltage, + .get_voltage = rpm_vreg_get_voltage, + .set_mode = rpm_vreg_set_mode, + .get_mode = rpm_vreg_get_mode, + .get_optimum_mode = rpm_vreg_get_optimum_mode, + .enable_time = rpm_vreg_enable_time, +}; + static struct regulator_ops switch_ops = { .enable = rpm_vreg_enable, .disable = rpm_vreg_disable, @@ -1426,6 +1467,7 @@ static struct regulator_ops bob_ops = { .get_voltage = rpm_vreg_get_voltage, .set_mode = rpm_vreg_set_bob_mode, .get_mode = rpm_vreg_get_bob_mode, + .get_optimum_mode = rpm_vreg_get_optimum_mode, .enable_time = rpm_vreg_enable_time, }; @@ -1676,6 +1718,12 @@ static int rpm_vreg_device_probe(struct platform_device *pdev) if (of_get_property(node, "parent-supply", NULL)) init_data->supply_regulator = "parent"; + of_property_read_u32(node, "qcom,pwm-threshold-current", + ®->hpm_threshold_current); + if (reg->hpm_threshold_current > 0 + && regulator_type == RPM_REGULATOR_TYPE_SMPS) + reg->rdesc.ops = &smps_optimum_mode_ops; + /* * Fill in ops and mode masks based on callbacks specified for * this type of regulator. @@ -1689,8 +1737,13 @@ static int rpm_vreg_device_probe(struct platform_device *pdev) if (reg->rdesc.ops->get_mode) { init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS; - init_data->constraints.valid_modes_mask - |= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE; + + if (regulator_type == RPM_REGULATOR_TYPE_BOB) + init_data->constraints.valid_modes_mask + = REGULATOR_MODE_FAST | REGULATOR_MODE_NORMAL; + else + init_data->constraints.valid_modes_mask + |= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE; } reg->rdesc.name = init_data->constraints.name; diff --git a/drivers/regulator/spm-regulator.c b/drivers/regulator/spm-regulator.c index 484f0412addf..d469463a0b00 100644 --- a/drivers/regulator/spm-regulator.c +++ b/drivers/regulator/spm-regulator.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -49,6 +49,7 @@ enum qpnp_regulator_uniq_type { QPNP_TYPE_HF, QPNP_TYPE_FTS2, QPNP_TYPE_FTS2p5, + QPNP_TYPE_FTS426, QPNP_TYPE_ULT_HF, }; @@ -56,6 +57,7 @@ enum qpnp_regulator_type { QPNP_HF_TYPE = 0x03, QPNP_FTS2_TYPE = 0x1C, QPNP_FTS2p5_TYPE = 0x1C, + QPNP_FTS426_TYPE = 0x1C, QPNP_ULT_HF_TYPE = 0x22, }; @@ -63,15 +65,22 @@ enum qpnp_regulator_subtype { QPNP_FTS2_SUBTYPE = 0x08, QPNP_HF_SUBTYPE = 0x08, QPNP_FTS2p5_SUBTYPE = 0x09, + QPNP_FTS426_SUBTYPE = 0x0A, QPNP_ULT_HF_SUBTYPE = 0x0D, }; +enum qpnp_logical_mode { + QPNP_LOGICAL_MODE_AUTO, + QPNP_LOGICAL_MODE_PWM, +}; + static const struct voltage_range fts2_range0 = {0, 350000, 1275000, 5000}; static const struct voltage_range fts2_range1 = {0, 700000, 2040000, 10000}; static const struct voltage_range fts2p5_range0 = { 80000, 350000, 1355000, 5000}; static const struct voltage_range fts2p5_range1 = {160000, 700000, 2200000, 10000}; +static const struct voltage_range fts426_range = {0, 320000, 1352000, 4000}; static const struct voltage_range ult_hf_range0 = {375000, 375000, 1562500, 12500}; static const struct voltage_range ult_hf_range1 = {750000, 750000, 1525000, @@ -86,17 +95,45 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000, #define QPNP_SMPS_REG_VOLTAGE_SETPOINT 0x41 #define QPNP_SMPS_REG_MODE 0x45 #define QPNP_SMPS_REG_STEP_CTRL 0x61 +#define QPNP_SMPS_REG_UL_LL_CTRL 0x68 + +/* FTS426 voltage control registers */ +#define QPNP_FTS426_REG_VOLTAGE_LB 0x40 +#define QPNP_FTS426_REG_VOLTAGE_UB 0x41 +#define QPNP_FTS426_REG_VOLTAGE_VALID_LB 0x42 +#define QPNP_FTS426_REG_VOLTAGE_VALID_UB 0x43 + +/* HF voltage limit registers */ +#define QPNP_HF_REG_VOLTAGE_ULS 0x69 +#define QPNP_HF_REG_VOLTAGE_LLS 0x6B + +/* FTS voltage limit registers */ +#define QPNP_FTS_REG_VOLTAGE_ULS_VALID 0x6A +#define QPNP_FTS_REG_VOLTAGE_LLS_VALID 0x6C + +/* FTS426 voltage limit registers */ +#define QPNP_FTS426_REG_VOLTAGE_ULS_LB 0x68 +#define QPNP_FTS426_REG_VOLTAGE_ULS_UB 0x69 + +/* Common regulator UL & LL limits control register layout */ +#define QPNP_COMMON_UL_EN_MASK 0x80 +#define QPNP_COMMON_LL_EN_MASK 0x40 #define QPNP_SMPS_MODE_PWM 0x80 #define QPNP_SMPS_MODE_AUTO 0x40 +#define QPNP_FTS426_MODE_PWM 0x07 +#define QPNP_FTS426_MODE_AUTO 0x06 #define QPNP_SMPS_STEP_CTRL_STEP_MASK 0x18 #define QPNP_SMPS_STEP_CTRL_STEP_SHIFT 3 #define QPNP_SMPS_STEP_CTRL_DELAY_MASK 0x07 #define QPNP_SMPS_STEP_CTRL_DELAY_SHIFT 0 +#define QPNP_FTS426_STEP_CTRL_DELAY_MASK 0x03 +#define QPNP_FTS426_STEP_CTRL_DELAY_SHIFT 0 /* Clock rate in kHz of the FTS2 regulator reference clock. */ #define QPNP_SMPS_CLOCK_RATE 19200 +#define QPNP_FTS426_CLOCK_RATE 4800 /* Time to delay in us to ensure that a mode change has completed. */ #define QPNP_FTS2_MODE_CHANGE_DELAY 50 @@ -107,6 +144,7 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000, /* Minimum voltage stepper delay for each step. */ #define QPNP_FTS2_STEP_DELAY 8 #define QPNP_HF_STEP_DELAY 20 +#define QPNP_FTS426_STEP_DELAY 2 /* Arbitrarily large max step size used to avoid possible numerical overflow */ #define SPM_REGULATOR_MAX_STEP_UV 10000000 @@ -117,6 +155,8 @@ static const struct voltage_range hf_range1 = {1550000, 1550000, 3125000, */ #define QPNP_FTS2_STEP_MARGIN_NUM 4 #define QPNP_FTS2_STEP_MARGIN_DEN 5 +#define QPNP_FTS426_STEP_MARGIN_NUM 10 +#define QPNP_FTS426_STEP_MARGIN_DEN 11 /* * Settling delay for FTS2.5 @@ -140,8 +180,8 @@ struct spm_vreg { u32 max_step_uV; bool online; u16 spmi_base_addr; - u8 init_mode; - u8 mode; + enum qpnp_logical_mode init_mode; + enum qpnp_logical_mode mode; int step_rate; enum qpnp_regulator_uniq_type regulator_type; u32 cpu_num; @@ -163,6 +203,9 @@ static int spm_regulator_uv_to_vlevel(struct spm_vreg *vreg, int uV) { int vlevel; + if (vreg->regulator_type == QPNP_TYPE_FTS426) + return roundup(uV, vreg->range->step_uV) / 1000; + vlevel = DIV_ROUND_UP(uV - vreg->range->min_uV, vreg->range->step_uV); /* Fix VSET for ULT HF Buck */ @@ -177,6 +220,8 @@ static int spm_regulator_uv_to_vlevel(struct spm_vreg *vreg, int uV) static int spm_regulator_vlevel_to_uv(struct spm_vreg *vreg, int vlevel) { + if (vreg->regulator_type == QPNP_TYPE_FTS426) + return vlevel * 1000; /* * Calculate ULT HF buck VSET based on range: * In case of range 0: VSET is a 7 bit value. @@ -204,32 +249,91 @@ static unsigned spm_regulator_vlevel_to_selector(struct spm_vreg *vreg, static int qpnp_smps_read_voltage(struct spm_vreg *vreg) { int rc; - u8 reg = 0; - uint val; + u8 val[2] = {0}; - rc = regmap_read(vreg->regmap, - vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT, - &val); - if (rc) { - dev_err(&vreg->pdev->dev, - "%s: could not read voltage setpoint register, rc=%d\n", - __func__, rc); - return rc; + if (vreg->regulator_type == QPNP_TYPE_FTS426) { + rc = regmap_bulk_read(vreg->regmap, + vreg->spmi_base_addr + QPNP_FTS426_REG_VOLTAGE_VALID_LB, + val, 2); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: could not read voltage setpoint registers, rc=%d\n", + __func__, rc); + return rc; + } + + vreg->last_set_vlevel = ((unsigned)val[1] << 8) | val[0]; + } else { + rc = regmap_bulk_read(vreg->regmap, + vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT, + val, 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: could not read voltage setpoint register, rc=%d\n", + __func__, rc); + return rc; + } + vreg->last_set_vlevel = val[0]; } - reg = (u8)val; - vreg->last_set_vlevel = reg; - vreg->last_set_uV = spm_regulator_vlevel_to_uv(vreg, reg); + vreg->last_set_uV = spm_regulator_vlevel_to_uv(vreg, + vreg->last_set_vlevel); + return rc; +} + +static int qpnp_smps_write_voltage(struct spm_vreg *vreg, unsigned vlevel) +{ + int rc = 0; + u8 reg[2]; + + /* Set voltage control registers via SPMI. */ + reg[0] = vlevel & 0xFF; + reg[1] = (vlevel >> 8) & 0xFF; + + if (vreg->regulator_type == QPNP_TYPE_FTS426) { + rc = regmap_bulk_write(vreg->regmap, + vreg->spmi_base_addr + QPNP_FTS426_REG_VOLTAGE_LB, + reg, 2); + } else { + rc = regmap_write(vreg->regmap, + vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT, + reg[0]); + } + + if (rc) + pr_err("%s: regmap_write failed, rc=%d\n", + vreg->rdesc.name, rc); return rc; } +static inline enum qpnp_logical_mode qpnp_regval_to_mode(struct spm_vreg *vreg, + u8 regval) +{ + if (vreg->regulator_type == QPNP_TYPE_FTS426) + return (regval == QPNP_FTS426_MODE_PWM) + ? QPNP_LOGICAL_MODE_PWM : QPNP_LOGICAL_MODE_AUTO; + else + return (regval & QPNP_SMPS_MODE_PWM) + ? QPNP_LOGICAL_MODE_PWM : QPNP_LOGICAL_MODE_AUTO; +} + +static inline u8 qpnp_mode_to_regval(struct spm_vreg *vreg, + enum qpnp_logical_mode mode) +{ + if (vreg->regulator_type == QPNP_TYPE_FTS426) + return (mode == QPNP_LOGICAL_MODE_PWM) + ? QPNP_FTS426_MODE_PWM : QPNP_FTS426_MODE_AUTO; + else + return (mode == QPNP_LOGICAL_MODE_PWM) + ? QPNP_SMPS_MODE_PWM : QPNP_SMPS_MODE_AUTO; +} + static int qpnp_smps_set_mode(struct spm_vreg *vreg, u8 mode) { int rc; rc = regmap_write(vreg->regmap, - vreg->spmi_base_addr + QPNP_SMPS_REG_MODE, mode); + vreg->spmi_base_addr + QPNP_SMPS_REG_MODE, + qpnp_mode_to_regval(vreg, mode)); if (rc) dev_err(&vreg->pdev->dev, "%s: could not write to mode register, rc=%d\n", @@ -275,7 +379,6 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV) bool spm_failed = false; int rc = 0; u32 slew_delay; - u8 reg; if (likely(!vreg->bypass_spm)) { /* Set voltage control register via SPM. */ @@ -288,13 +391,9 @@ static int spm_regulator_write_voltage(struct spm_vreg *vreg, int uV) } if (unlikely(vreg->bypass_spm || spm_failed)) { - /* Set voltage control register via SPMI. */ - reg = vlevel; - rc = regmap_write(vreg->regmap, - vreg->spmi_base_addr + QPNP_SMPS_REG_VOLTAGE_SETPOINT, - reg); + rc = qpnp_smps_write_voltage(vreg, vlevel); if (rc) { - pr_err("%s: regmap_write failed, rc=%d\n", + pr_err("%s: voltage write failed, rc=%d\n", vreg->rdesc.name, rc); return rc; } @@ -351,12 +450,12 @@ static int _spm_regulator_set_voltage(struct regulator_dev *rdev) return 0; pwm_required = (vreg->regulator_type == QPNP_TYPE_FTS2) - && !(vreg->init_mode & QPNP_SMPS_MODE_PWM) + && (vreg->init_mode != QPNP_LOGICAL_MODE_PWM) && vreg->uV > vreg->last_set_uV; if (pwm_required) { /* Switch to PWM mode so that voltage ramping is fast. */ - rc = qpnp_smps_set_mode(vreg, QPNP_SMPS_MODE_PWM); + rc = qpnp_smps_set_mode(vreg, QPNP_LOGICAL_MODE_PWM); if (rc) return rc; } @@ -375,7 +474,7 @@ static int _spm_regulator_set_voltage(struct regulator_dev *rdev) /* Wait for mode transition to complete. */ udelay(QPNP_FTS2_MODE_CHANGE_DELAY - QPNP_SPMI_WRITE_MIN_DELAY); /* Switch to AUTO mode so that power consumption is lowered. */ - rc = qpnp_smps_set_mode(vreg, QPNP_SMPS_MODE_AUTO); + rc = qpnp_smps_set_mode(vreg, QPNP_LOGICAL_MODE_AUTO); if (rc) return rc; } @@ -466,7 +565,7 @@ static unsigned int spm_regulator_get_mode(struct regulator_dev *rdev) { struct spm_vreg *vreg = rdev_get_drvdata(rdev); - return vreg->mode == QPNP_SMPS_MODE_PWM + return vreg->mode == QPNP_LOGICAL_MODE_PWM ? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE; } @@ -480,8 +579,8 @@ static int spm_regulator_set_mode(struct regulator_dev *rdev, unsigned int mode) * in the case that qcom,mode has been specified as "pwm" in device * tree. */ - vreg->mode - = mode == REGULATOR_MODE_NORMAL ? QPNP_SMPS_MODE_PWM : vreg->init_mode; + vreg->mode = (mode == REGULATOR_MODE_NORMAL) ? QPNP_LOGICAL_MODE_PWM + : vreg->init_mode; return qpnp_smps_set_mode(vreg, vreg->mode); } @@ -538,7 +637,7 @@ static int spm_regulator_avs_set_voltage(struct regulator_dev *rdev, int min_uV, return -EINVAL; } - vlevel_max = (uV - range->min_uV) / range->step_uV; + vlevel_max = spm_regulator_uv_to_vlevel(vreg, uV); avs_max_uV = spm_regulator_vlevel_to_uv(vreg, vlevel_max); if (avs_max_uV < min_uV) { @@ -646,6 +745,9 @@ static int qpnp_smps_check_type(struct spm_vreg *vreg) } else if (type[0] == QPNP_FTS2p5_TYPE && type[1] == QPNP_FTS2p5_SUBTYPE) { vreg->regulator_type = QPNP_TYPE_FTS2p5; + } else if (type[0] == QPNP_FTS426_TYPE + && type[1] == QPNP_FTS426_SUBTYPE) { + vreg->regulator_type = QPNP_TYPE_FTS426; } else if (type[0] == QPNP_ULT_HF_TYPE && type[1] == QPNP_ULT_HF_SUBTYPE) { vreg->regulator_type = QPNP_TYPE_ULT_HF; @@ -750,10 +852,10 @@ static int qpnp_smps_init_mode(struct spm_vreg *vreg) &mode_name); if (!rc) { if (strcmp("pwm", mode_name) == 0) { - vreg->init_mode = QPNP_SMPS_MODE_PWM; + vreg->init_mode = QPNP_LOGICAL_MODE_PWM; } else if ((strcmp("auto", mode_name) == 0) && (vreg->regulator_type != QPNP_TYPE_ULT_HF)) { - vreg->init_mode = QPNP_SMPS_MODE_AUTO; + vreg->init_mode = QPNP_LOGICAL_MODE_AUTO; } else { dev_err(&vreg->pdev->dev, "%s: unknown regulator mode: %s\n", @@ -761,13 +863,9 @@ static int qpnp_smps_init_mode(struct spm_vreg *vreg) return -EINVAL; } - rc = regmap_write(vreg->regmap, - vreg->spmi_base_addr + QPNP_SMPS_REG_MODE, - *&vreg->init_mode); + rc = qpnp_smps_set_mode(vreg, vreg->init_mode); if (rc) - dev_err(&vreg->pdev->dev, - "%s: could not write mode register, rc=%d\n", - __func__, rc); + return rc; } else { rc = regmap_read(vreg->regmap, vreg->spmi_base_addr + QPNP_SMPS_REG_MODE, @@ -776,7 +874,7 @@ static int qpnp_smps_init_mode(struct spm_vreg *vreg) dev_err(&vreg->pdev->dev, "%s: could not read mode register, rc=%d\n", __func__, rc); - vreg->init_mode = (u8)val; + vreg->init_mode = qpnp_regval_to_mode(vreg, val); } vreg->mode = vreg->init_mode; @@ -801,26 +899,41 @@ static int qpnp_smps_init_step_rate(struct spm_vreg *vreg) } reg = (u8)val; - /* ULT buck does not support steps */ - if (vreg->regulator_type != QPNP_TYPE_ULT_HF) + /* ULT and FTS426 bucks do not support steps */ + if (vreg->regulator_type != QPNP_TYPE_ULT_HF && vreg->regulator_type != + QPNP_TYPE_FTS426) step = (reg & QPNP_SMPS_STEP_CTRL_STEP_MASK) >> QPNP_SMPS_STEP_CTRL_STEP_SHIFT; - delay = (reg & QPNP_SMPS_STEP_CTRL_DELAY_MASK) - >> QPNP_SMPS_STEP_CTRL_DELAY_SHIFT; + if (vreg->regulator_type == QPNP_TYPE_FTS426) { + delay = (reg & QPNP_FTS426_STEP_CTRL_DELAY_MASK) + >> QPNP_FTS426_STEP_CTRL_DELAY_SHIFT; - /* step_rate has units of uV/us. */ - vreg->step_rate = QPNP_SMPS_CLOCK_RATE * vreg->range->step_uV - * (1 << step); + /* step_rate has units of uV/us. */ + vreg->step_rate = QPNP_FTS426_CLOCK_RATE * vreg->range->step_uV; + } else { + delay = (reg & QPNP_SMPS_STEP_CTRL_DELAY_MASK) + >> QPNP_SMPS_STEP_CTRL_DELAY_SHIFT; + + /* step_rate has units of uV/us. */ + vreg->step_rate = QPNP_SMPS_CLOCK_RATE * vreg->range->step_uV + * (1 << step); + } if ((vreg->regulator_type == QPNP_TYPE_ULT_HF) || (vreg->regulator_type == QPNP_TYPE_HF)) vreg->step_rate /= 1000 * (QPNP_HF_STEP_DELAY << delay); + else if (vreg->regulator_type == QPNP_TYPE_FTS426) + vreg->step_rate /= 1000 * (QPNP_FTS426_STEP_DELAY << delay); else vreg->step_rate /= 1000 * (QPNP_FTS2_STEP_DELAY << delay); - vreg->step_rate = vreg->step_rate * QPNP_FTS2_STEP_MARGIN_NUM - / QPNP_FTS2_STEP_MARGIN_DEN; + if (vreg->regulator_type == QPNP_TYPE_FTS426) + vreg->step_rate = vreg->step_rate * QPNP_FTS426_STEP_MARGIN_NUM + / QPNP_FTS426_STEP_MARGIN_DEN; + else + vreg->step_rate = vreg->step_rate * QPNP_FTS2_STEP_MARGIN_NUM + / QPNP_FTS2_STEP_MARGIN_DEN; /* Ensure that the stepping rate is greater than 0. */ vreg->step_rate = max(vreg->step_rate, 1); @@ -828,10 +941,93 @@ static int qpnp_smps_init_step_rate(struct spm_vreg *vreg) return rc; } +static int qpnp_smps_check_constraints(struct spm_vreg *vreg, + struct regulator_init_data *init_data) +{ + int rc = 0, limit_min_uV, limit_max_uV; + u16 ul_reg, ll_reg; + u8 reg[2]; + + limit_min_uV = 0; + limit_max_uV = INT_MAX; + + ul_reg = QPNP_FTS_REG_VOLTAGE_ULS_VALID; + ll_reg = QPNP_FTS_REG_VOLTAGE_LLS_VALID; + + switch (vreg->regulator_type) { + case QPNP_TYPE_HF: + ul_reg = QPNP_HF_REG_VOLTAGE_ULS; + ll_reg = QPNP_HF_REG_VOLTAGE_LLS; + case QPNP_TYPE_FTS2: + case QPNP_TYPE_FTS2p5: + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + QPNP_SMPS_REG_UL_LL_CTRL, reg, 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: UL_LL register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + if (reg[0] & QPNP_COMMON_UL_EN_MASK) { + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + ul_reg, ®[1], 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: ULS register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + limit_max_uV = spm_regulator_vlevel_to_uv(vreg, reg[1]); + } + + if (reg[0] & QPNP_COMMON_LL_EN_MASK) { + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + ll_reg, ®[1], 1); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: LLS register read failed, rc=%d\n", + __func__, rc); + return rc; + } + + limit_min_uV = spm_regulator_vlevel_to_uv(vreg, reg[1]); + } + + break; + case QPNP_TYPE_FTS426: + rc = regmap_bulk_read(vreg->regmap, vreg->spmi_base_addr + + QPNP_FTS426_REG_VOLTAGE_ULS_LB, + reg, 2); + if (rc) { + dev_err(&vreg->pdev->dev, "%s: could not read voltage limit registers, rc=%d\n", + __func__, rc); + return rc; + } + + limit_max_uV = spm_regulator_vlevel_to_uv(vreg, + ((unsigned)reg[1] << 8) | reg[0]); + break; + case QPNP_TYPE_ULT_HF: + /* no HW voltage limit configuration */ + break; + } + + if (init_data->constraints.min_uV < limit_min_uV + || init_data->constraints.max_uV > limit_max_uV) { + dev_err(&vreg->pdev->dev, "regulator min/max(%d/%d) constraints do not fit within HW configured min/max(%d/%d) constraints\n", + init_data->constraints.min_uV, + init_data->constraints.max_uV, limit_min_uV, + limit_max_uV); + return -EINVAL; + } + + return rc; +} + static bool spm_regulator_using_range0(struct spm_vreg *vreg) { return vreg->range == &fts2_range0 || vreg->range == &fts2p5_range0 - || vreg->range == &ult_hf_range0 || vreg->range == &hf_range0; + || vreg->range == &ult_hf_range0 || vreg->range == &hf_range0 + || vreg->range == &fts426_range; } /* Register a regulator to enable/disable AVS and set AVS min/max limits. */ @@ -969,6 +1165,8 @@ static int spm_regulator_probe(struct platform_device *pdev) rc = qpnp_smps_init_range(vreg, &fts2_range0, &fts2_range1); else if (vreg->regulator_type == QPNP_TYPE_FTS2p5) rc = qpnp_smps_init_range(vreg, &fts2p5_range0, &fts2p5_range1); + else if (vreg->regulator_type == QPNP_TYPE_FTS426) + vreg->range = &fts426_range; else if (vreg->regulator_type == QPNP_TYPE_HF) rc = qpnp_smps_init_range(vreg, &hf_range0, &hf_range1); else if (vreg->regulator_type == QPNP_TYPE_ULT_HF) @@ -1006,6 +1204,13 @@ static int spm_regulator_probe(struct platform_device *pdev) return -EINVAL; } + rc = qpnp_smps_check_constraints(vreg, init_data); + if (rc) { + dev_err(&pdev->dev, "%s: regulator constraints check failed, rc=%d\n", + __func__, rc); + return rc; + } + vreg->rdesc.name = init_data->constraints.name; vreg->rdesc.type = REGULATOR_VOLTAGE; vreg->rdesc.owner = THIS_MODULE; @@ -1050,8 +1255,8 @@ static int spm_regulator_probe(struct platform_device *pdev) vreg->rdesc.name, spm_regulator_using_range0(vreg) ? "LV" : "MV", vreg->uV, - vreg->init_mode & QPNP_SMPS_MODE_PWM ? "PWM" : - (vreg->init_mode & QPNP_SMPS_MODE_AUTO ? "AUTO" : "PFM"), + vreg->init_mode == QPNP_LOGICAL_MODE_PWM ? "PWM" : + (vreg->init_mode == QPNP_LOGICAL_MODE_AUTO ? "AUTO" : "PFM"), vreg->step_rate); return rc; diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index d2c3d7cc35f5..5ca6d2130593 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -311,8 +311,7 @@ static int tps_65023_probe(struct i2c_client *client, /* Enable setting output voltage by I2C */ regmap_update_bits(tps->regmap, TPS65023_REG_CON_CTRL2, - TPS65023_REG_CTRL2_CORE_ADJ, - TPS65023_REG_CTRL2_CORE_ADJ); + TPS65023_REG_CTRL2_CORE_ADJ, 0); return 0; } diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 1766a20ebcb1..741f3ee81cfe 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -717,6 +717,7 @@ enum qeth_discipline_id { }; struct qeth_discipline { + const struct device_type *devtype; void (*start_poll)(struct ccw_device *, int, unsigned long); qdio_handler_t *input_handler; qdio_handler_t *output_handler; @@ -881,6 +882,9 @@ extern struct qeth_discipline qeth_l2_discipline; extern struct qeth_discipline qeth_l3_discipline; extern const struct attribute_group *qeth_generic_attr_groups[]; extern const struct attribute_group *qeth_osn_attr_groups[]; +extern const struct attribute_group qeth_device_attr_group; +extern const struct attribute_group qeth_device_blkt_group; +extern const struct device_type qeth_generic_devtype; extern struct workqueue_struct *qeth_wq; int qeth_card_hw_is_reachable(struct qeth_card *); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index 31ac53fa5cee..d10bf3da8e5f 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -5449,10 +5449,12 @@ void qeth_core_free_discipline(struct qeth_card *card) card->discipline = NULL; } -static const struct device_type qeth_generic_devtype = { +const struct device_type qeth_generic_devtype = { .name = "qeth_generic", .groups = qeth_generic_attr_groups, }; +EXPORT_SYMBOL_GPL(qeth_generic_devtype); + static const struct device_type qeth_osn_devtype = { .name = "qeth_osn", .groups = qeth_osn_attr_groups, @@ -5578,23 +5580,22 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev) goto err_card; } - if (card->info.type == QETH_CARD_TYPE_OSN) - gdev->dev.type = &qeth_osn_devtype; - else - gdev->dev.type = &qeth_generic_devtype; - switch (card->info.type) { case QETH_CARD_TYPE_OSN: case QETH_CARD_TYPE_OSM: rc = qeth_core_load_discipline(card, QETH_DISCIPLINE_LAYER2); if (rc) goto err_card; + + gdev->dev.type = (card->info.type != QETH_CARD_TYPE_OSN) + ? card->discipline->devtype + : &qeth_osn_devtype; rc = card->discipline->setup(card->gdev); if (rc) goto err_disc; - case QETH_CARD_TYPE_OSD: - case QETH_CARD_TYPE_OSX: + break; default: + gdev->dev.type = &qeth_generic_devtype; break; } @@ -5650,8 +5651,10 @@ static int qeth_core_set_online(struct ccwgroup_device *gdev) if (rc) goto err; rc = card->discipline->setup(card->gdev); - if (rc) + if (rc) { + qeth_core_free_discipline(card); goto err; + } } rc = card->discipline->set_online(gdev); err: diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index e6e5b9671bf2..fa844b0ff847 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -409,12 +409,16 @@ static ssize_t qeth_dev_layer2_store(struct device *dev, if (card->options.layer2 == newdis) goto out; - else { - card->info.mac_bits = 0; - if (card->discipline) { - card->discipline->remove(card->gdev); - qeth_core_free_discipline(card); - } + if (card->info.type == QETH_CARD_TYPE_OSM) { + /* fixed layer, can't switch */ + rc = -EOPNOTSUPP; + goto out; + } + + card->info.mac_bits = 0; + if (card->discipline) { + card->discipline->remove(card->gdev); + qeth_core_free_discipline(card); } rc = qeth_core_load_discipline(card, newdis); @@ -422,6 +426,8 @@ static ssize_t qeth_dev_layer2_store(struct device *dev, goto out; rc = card->discipline->setup(card->gdev); + if (rc) + qeth_core_free_discipline(card); out: mutex_unlock(&card->discipline_mutex); return rc ? rc : count; @@ -699,10 +705,11 @@ static struct attribute *qeth_blkt_device_attrs[] = { &dev_attr_inter_jumbo.attr, NULL, }; -static struct attribute_group qeth_device_blkt_group = { +const struct attribute_group qeth_device_blkt_group = { .name = "blkt", .attrs = qeth_blkt_device_attrs, }; +EXPORT_SYMBOL_GPL(qeth_device_blkt_group); static struct attribute *qeth_device_attrs[] = { &dev_attr_state.attr, @@ -722,9 +729,10 @@ static struct attribute *qeth_device_attrs[] = { &dev_attr_switch_attrs.attr, NULL, }; -static struct attribute_group qeth_device_attr_group = { +const struct attribute_group qeth_device_attr_group = { .attrs = qeth_device_attrs, }; +EXPORT_SYMBOL_GPL(qeth_device_attr_group); const struct attribute_group *qeth_generic_attr_groups[] = { &qeth_device_attr_group, diff --git a/drivers/s390/net/qeth_l2.h b/drivers/s390/net/qeth_l2.h index 0767556404bd..eb87bf97d38a 100644 --- a/drivers/s390/net/qeth_l2.h +++ b/drivers/s390/net/qeth_l2.h @@ -8,6 +8,8 @@ #include "qeth_core.h" +extern const struct attribute_group *qeth_l2_attr_groups[]; + int qeth_l2_create_device_attributes(struct device *); void qeth_l2_remove_device_attributes(struct device *); void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card); diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index df036b872b05..bf1e0e39334d 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -1027,11 +1027,21 @@ static int qeth_l2_stop(struct net_device *dev) return 0; } +static const struct device_type qeth_l2_devtype = { + .name = "qeth_layer2", + .groups = qeth_l2_attr_groups, +}; + static int qeth_l2_probe_device(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); + int rc; - qeth_l2_create_device_attributes(&gdev->dev); + if (gdev->dev.type == &qeth_generic_devtype) { + rc = qeth_l2_create_device_attributes(&gdev->dev); + if (rc) + return rc; + } INIT_LIST_HEAD(&card->vid_list); hash_init(card->mac_htable); card->options.layer2 = 1; @@ -1043,7 +1053,8 @@ static void qeth_l2_remove_device(struct ccwgroup_device *cgdev) { struct qeth_card *card = dev_get_drvdata(&cgdev->dev); - qeth_l2_remove_device_attributes(&cgdev->dev); + if (cgdev->dev.type == &qeth_generic_devtype) + qeth_l2_remove_device_attributes(&cgdev->dev); qeth_set_allowed_threads(card, 0, 1); wait_event(card->wait_q, qeth_threads_running(card, 0xffffffff) == 0); @@ -1101,7 +1112,6 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) case QETH_CARD_TYPE_OSN: card->dev = alloc_netdev(0, "osn%d", NET_NAME_UNKNOWN, ether_setup); - card->dev->flags |= IFF_NOARP; break; default: card->dev = alloc_etherdev(0); @@ -1114,9 +1124,12 @@ static int qeth_l2_setup_netdev(struct qeth_card *card) card->dev->watchdog_timeo = QETH_TX_TIMEOUT; card->dev->mtu = card->info.initial_mtu; card->dev->netdev_ops = &qeth_l2_netdev_ops; - card->dev->ethtool_ops = - (card->info.type != QETH_CARD_TYPE_OSN) ? - &qeth_l2_ethtool_ops : &qeth_l2_osn_ops; + if (card->info.type == QETH_CARD_TYPE_OSN) { + card->dev->ethtool_ops = &qeth_l2_osn_ops; + card->dev->flags |= IFF_NOARP; + } else { + card->dev->ethtool_ops = &qeth_l2_ethtool_ops; + } card->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; if (card->info.type == QETH_CARD_TYPE_OSD && !card->info.guestlan) { card->dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM; @@ -1429,6 +1442,7 @@ static int qeth_l2_control_event(struct qeth_card *card, } struct qeth_discipline qeth_l2_discipline = { + .devtype = &qeth_l2_devtype, .start_poll = qeth_qdio_start_poll, .input_handler = (qdio_handler_t *) qeth_qdio_input_handler, .output_handler = (qdio_handler_t *) qeth_qdio_output_handler, diff --git a/drivers/s390/net/qeth_l2_sys.c b/drivers/s390/net/qeth_l2_sys.c index 692db49e3d2a..a48ed9e7e168 100644 --- a/drivers/s390/net/qeth_l2_sys.c +++ b/drivers/s390/net/qeth_l2_sys.c @@ -272,3 +272,11 @@ void qeth_l2_setup_bridgeport_attrs(struct qeth_card *card) } else qeth_bridgeport_an_set(card, 0); } + +const struct attribute_group *qeth_l2_attr_groups[] = { + &qeth_device_attr_group, + &qeth_device_blkt_group, + /* l2 specific, see l2_{create,remove}_device_attributes(): */ + &qeth_l2_bridgeport_attr_group, + NULL, +}; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index cc4d3c3d8cc5..285fe0b2c753 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -3227,8 +3227,11 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) static int qeth_l3_probe_device(struct ccwgroup_device *gdev) { struct qeth_card *card = dev_get_drvdata(&gdev->dev); + int rc; - qeth_l3_create_device_attributes(&gdev->dev); + rc = qeth_l3_create_device_attributes(&gdev->dev); + if (rc) + return rc; card->options.layer2 = 0; card->info.hwtrap = 0; return 0; @@ -3519,6 +3522,7 @@ static int qeth_l3_control_event(struct qeth_card *card, } struct qeth_discipline qeth_l3_discipline = { + .devtype = &qeth_generic_devtype, .start_poll = qeth_qdio_start_poll, .input_handler = (qdio_handler_t *) qeth_qdio_input_handler, .output_handler = (qdio_handler_t *) qeth_qdio_output_handler, diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 8a5fbdb45cfd..e333029e4b6c 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -4452,6 +4452,7 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) struct MPT3SAS_DEVICE *sas_device_priv_data; u32 response_code = 0; unsigned long flags; + unsigned int sector_sz; mpi_reply = mpt3sas_base_get_reply_virt_addr(ioc, reply); scmd = _scsih_scsi_lookup_get_clear(ioc, smid); @@ -4510,6 +4511,20 @@ _scsih_io_done(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply) } xfer_cnt = le32_to_cpu(mpi_reply->TransferCount); + + /* In case of bogus fw or device, we could end up having + * unaligned partial completion. We can force alignment here, + * then scsi-ml does not need to handle this misbehavior. + */ + sector_sz = scmd->device->sector_size; + if (unlikely(scmd->request->cmd_type == REQ_TYPE_FS && sector_sz && + xfer_cnt % sector_sz)) { + sdev_printk(KERN_INFO, scmd->device, + "unaligned partial completion avoided (xfer_cnt=%u, sector_sz=%u)\n", + xfer_cnt, sector_sz); + xfer_cnt = round_down(xfer_cnt, sector_sz); + } + scsi_set_resid(scmd, scsi_bufflen(scmd) - xfer_cnt); if (ioc_status & MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) log_info = le32_to_cpu(mpi_reply->IOCLogInfo); diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 3588a56aabb4..5cbf20ab94aa 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -2311,10 +2311,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (mem_only) { if (pci_enable_device_mem(pdev)) - goto probe_out; + return ret; } else { if (pci_enable_device(pdev)) - goto probe_out; + return ret; } /* This may fail but that's ok */ @@ -2324,7 +2324,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (!ha) { ql_log_pci(ql_log_fatal, pdev, 0x0009, "Unable to allocate memory for ha.\n"); - goto probe_out; + goto disable_device; } ql_dbg_pci(ql_dbg_init, pdev, 0x000a, "Memory allocated for ha=%p.\n", ha); @@ -2923,7 +2923,7 @@ iospace_config_failed: kfree(ha); ha = NULL; -probe_out: +disable_device: pci_disable_device(pdev); return ret; } diff --git a/drivers/scsi/ufs/ufs-qcom-ice.c b/drivers/scsi/ufs/ufs-qcom-ice.c index 0c862639fa3e..84765b17086c 100644 --- a/drivers/scsi/ufs/ufs-qcom-ice.c +++ b/drivers/scsi/ufs/ufs-qcom-ice.c @@ -170,17 +170,15 @@ out: static void ufs_qcom_ice_cfg_work(struct work_struct *work) { unsigned long flags; - struct ice_data_setting ice_set; struct ufs_qcom_host *qcom_host = container_of(work, struct ufs_qcom_host, ice_cfg_work); - struct request *req_pending = NULL; if (!qcom_host->ice.vops->config_start) return; spin_lock_irqsave(&qcom_host->ice_work_lock, flags); - req_pending = qcom_host->req_pending; - if (!req_pending) { + if (!qcom_host->req_pending) { + qcom_host->work_pending = false; spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags); return; } @@ -189,24 +187,15 @@ static void ufs_qcom_ice_cfg_work(struct work_struct *work) /* * config_start is called again as previous attempt returned -EAGAIN, * this call shall now take care of the necessary key setup. - * 'ice_set' will not actually be used, instead the next call to - * config_start() for this request, in the normal call flow, will - * succeed as the key has now been setup. */ qcom_host->ice.vops->config_start(qcom_host->ice.pdev, - qcom_host->req_pending, &ice_set, false); + qcom_host->req_pending, NULL, false); spin_lock_irqsave(&qcom_host->ice_work_lock, flags); qcom_host->req_pending = NULL; + qcom_host->work_pending = false; spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags); - /* - * Resume with requests processing. We assume config_start has been - * successful, but even if it wasn't we still must resume in order to - * allow for the request to be retried. - */ - ufshcd_scsi_unblock_requests(qcom_host->hba); - } /** @@ -285,18 +274,14 @@ int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host, * requires a non-atomic context, this means we should * call the function again from the worker thread to do * the configuration. For this request the error will - * propagate so it will be re-queued and until the - * configuration is is completed we block further - * request processing. + * propagate so it will be re-queued. */ if (err == -EAGAIN) { dev_dbg(qcom_host->hba->dev, "%s: scheduling task for ice setup\n", __func__); - if (!qcom_host->req_pending) { - ufshcd_scsi_block_requests( - qcom_host->hba); + if (!qcom_host->work_pending) { qcom_host->req_pending = cmd->request; if (!schedule_work( @@ -307,10 +292,9 @@ int ufs_qcom_ice_req_setup(struct ufs_qcom_host *qcom_host, &qcom_host->ice_work_lock, flags); - ufshcd_scsi_unblock_requests( - qcom_host->hba); return err; } + qcom_host->work_pending = true; } } else { @@ -409,9 +393,7 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host, * requires a non-atomic context, this means we should * call the function again from the worker thread to do * the configuration. For this request the error will - * propagate so it will be re-queued and until the - * configuration is is completed we block further - * request processing. + * propagate so it will be re-queued. */ if (err == -EAGAIN) { @@ -419,9 +401,8 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host, "%s: scheduling task for ice setup\n", __func__); - if (!qcom_host->req_pending) { - ufshcd_scsi_block_requests( - qcom_host->hba); + if (!qcom_host->work_pending) { + qcom_host->req_pending = cmd->request; if (!schedule_work( &qcom_host->ice_cfg_work)) { @@ -431,10 +412,9 @@ int ufs_qcom_ice_cfg_start(struct ufs_qcom_host *qcom_host, &qcom_host->ice_work_lock, flags); - ufshcd_scsi_unblock_requests( - qcom_host->hba); return err; } + qcom_host->work_pending = true; } } else { diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index aadaef7d1bed..aae796678ffe 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -2093,9 +2093,6 @@ static int ufs_qcom_init(struct ufs_hba *hba) struct ufs_qcom_host *host; struct resource *res; - if (strlen(android_boot_dev) && strcmp(android_boot_dev, dev_name(dev))) - return -ENODEV; - host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); if (!host) { err = -ENOMEM; @@ -2787,6 +2784,24 @@ static int ufs_qcom_probe(struct platform_device *pdev) { int err; struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + + /* + * On qcom platforms, bootdevice is the primary storage + * device. This device can either be eMMC or UFS. + * The type of device connected is detected at runtime. + * So, if an eMMC device is connected, and this function + * is invoked, it would turn-off the regulator if it detects + * that the storage device is not ufs. + * These regulators are turned ON by the bootloaders & turning + * them off without sending PON may damage the connected device. + * Hence, check for the connected device early-on & don't turn-off + * the regulators. + */ + if (of_property_read_bool(np, "non-removable") && + strlen(android_boot_dev) && + strcmp(android_boot_dev, dev_name(dev))) + return -ENODEV; /* Perform generic probe */ err = ufshcd_pltfrm_init(pdev, &ufs_hba_qcom_variant); diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h index 12154b268132..460213536e14 100644 --- a/drivers/scsi/ufs/ufs-qcom.h +++ b/drivers/scsi/ufs/ufs-qcom.h @@ -374,6 +374,7 @@ struct ufs_qcom_host { struct work_struct ice_cfg_work; struct request *req_pending; struct ufs_vreg *vddp_ref_clk; + bool work_pending; }; static inline u32 diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index a90c51a113d2..a2f2cc0c2c51 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -365,6 +365,8 @@ static int ufshcd_disable_clocks(struct ufs_hba *hba, bool is_gating_context); static int ufshcd_disable_clocks_skip_ref_clk(struct ufs_hba *hba, bool is_gating_context); +static void ufshcd_hold_all(struct ufs_hba *hba); +static void ufshcd_release_all(struct ufs_hba *hba); static int ufshcd_set_vccq_rail_unused(struct ufs_hba *hba, bool unused); static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba); static inline void ufshcd_save_tstamp_of_last_dme_cmd(struct ufs_hba *hba); @@ -516,7 +518,7 @@ static inline void ufshcd_remove_non_printable(char *val) *val = ' '; } -#define UFSHCD_MAX_CMD_LOGGING 100 +#define UFSHCD_MAX_CMD_LOGGING 200 #ifdef CONFIG_TRACEPOINTS static inline void ufshcd_add_command_trace(struct ufs_hba *hba, @@ -592,7 +594,7 @@ static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id) ufshcd_cmd_log(hba, str, "dme", 0xff, cmd_id, 0xff); } -static void ufshcd_cmd_log_print(struct ufs_hba *hba) +static void ufshcd_print_cmd_log(struct ufs_hba *hba) { int i; int pos; @@ -641,7 +643,7 @@ static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id) { } -static void ufshcd_cmd_log_print(struct ufs_hba *hba) +static void ufshcd_print_cmd_log(struct ufs_hba *hba) { } #endif @@ -651,7 +653,7 @@ static inline void ufshcd_cond_add_cmd_trace(struct ufs_hba *hba, unsigned int tag, const char *str) { struct ufshcd_lrb *lrbp; - char *cmd_type; + char *cmd_type = NULL; u8 opcode = 0; u8 cmd_id = 0, idn = 0; sector_t lba = -1; @@ -2038,6 +2040,22 @@ out: return; } +static void __ufshcd_set_auto_hibern8_timer(struct ufs_hba *hba, + unsigned long delay_ms) +{ + pm_runtime_get_sync(hba->dev); + ufshcd_hold_all(hba); + ufshcd_scsi_block_requests(hba); + down_write(&hba->lock); + /* wait for all the outstanding requests to finish */ + ufshcd_wait_for_doorbell_clr(hba, U64_MAX); + ufshcd_set_auto_hibern8_timer(hba, delay_ms); + up_write(&hba->lock); + ufshcd_scsi_unblock_requests(hba); + ufshcd_release_all(hba); + pm_runtime_put_sync(hba->dev); +} + static void ufshcd_hibern8_exit_work(struct work_struct *work) { int ret; @@ -2089,19 +2107,32 @@ static ssize_t ufshcd_hibern8_on_idle_delay_store(struct device *dev, { struct ufs_hba *hba = dev_get_drvdata(dev); unsigned long flags, value; + bool change = true; if (kstrtoul(buf, 0, &value)) return -EINVAL; spin_lock_irqsave(hba->host->host_lock, flags); + if (hba->hibern8_on_idle.delay_ms == value) + change = false; + + if (value >= hba->clk_gating.delay_ms_pwr_save || + value >= hba->clk_gating.delay_ms_perf) { + dev_err(hba->dev, "hibern8_on_idle_delay (%lu) can not be >= to clkgate_delay_ms_pwr_save (%lu) and clkgate_delay_ms_perf (%lu)\n", + value, hba->clk_gating.delay_ms_pwr_save, + hba->clk_gating.delay_ms_perf); + spin_unlock_irqrestore(hba->host->host_lock, flags); + return -EINVAL; + } + hba->hibern8_on_idle.delay_ms = value; spin_unlock_irqrestore(hba->host->host_lock, flags); /* Update auto hibern8 timer value if supported */ - if (ufshcd_is_auto_hibern8_supported(hba) && + if (change && ufshcd_is_auto_hibern8_supported(hba) && hba->hibern8_on_idle.is_enabled) - ufshcd_set_auto_hibern8_timer(hba, - hba->hibern8_on_idle.delay_ms); + __ufshcd_set_auto_hibern8_timer(hba, + hba->hibern8_on_idle.delay_ms); return count; } @@ -2131,7 +2162,7 @@ static ssize_t ufshcd_hibern8_on_idle_enable_store(struct device *dev, /* Update auto hibern8 timer value if supported */ if (ufshcd_is_auto_hibern8_supported(hba)) { - ufshcd_set_auto_hibern8_timer(hba, + __ufshcd_set_auto_hibern8_timer(hba, value ? hba->hibern8_on_idle.delay_ms : value); goto update; } @@ -3285,8 +3316,10 @@ static int ufshcd_exec_dev_cmd(struct ufs_hba *hba, /* * May get invoked from shutdown and IOCTL contexts. * In shutdown context, it comes in with lock acquired. + * In error recovery context, it may come with lock acquired. */ - if (!ufshcd_is_shutdown_ongoing(hba)) + + if (!ufshcd_is_shutdown_ongoing(hba) && !ufshcd_eh_in_progress(hba)) down_read(&hba->lock); /* @@ -3320,7 +3353,7 @@ static int ufshcd_exec_dev_cmd(struct ufs_hba *hba, out_put_tag: ufshcd_put_dev_cmd_tag(hba, tag); wake_up(&hba->dev_cmd.tag_wq); - if (!ufshcd_is_shutdown_ongoing(hba)) + if (!ufshcd_is_shutdown_ongoing(hba) && !ufshcd_eh_in_progress(hba)) up_read(&hba->lock); return err; } @@ -4268,6 +4301,7 @@ out: ufshcd_print_host_state(hba); ufshcd_print_pwr_info(hba); ufshcd_print_host_regs(hba); + ufshcd_print_cmd_log(hba); } ufshcd_save_tstamp_of_last_dme_cmd(hba); @@ -6096,7 +6130,7 @@ static void ufshcd_err_handler(struct work_struct *work) ufshcd_print_host_state(hba); ufshcd_print_pwr_info(hba); ufshcd_print_tmrs(hba, hba->outstanding_tasks); - ufshcd_cmd_log_print(hba); + ufshcd_print_cmd_log(hba); spin_lock_irqsave(hba->host->host_lock, flags); } } @@ -6608,7 +6642,7 @@ static int ufshcd_eh_device_reset_handler(struct scsi_cmnd *cmd) hba = shost_priv(host); tag = cmd->request->tag; - ufshcd_cmd_log_print(hba); + ufshcd_print_cmd_log(hba); lrbp = &hba->lrb[tag]; err = ufshcd_issue_tm_cmd(hba, lrbp->lun, 0, UFS_LOGICAL_RESET, &resp); if (err || resp != UPIU_TASK_MANAGEMENT_FUNC_COMPL) { diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index ea008ffbc856..0c4414d27eeb 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -468,6 +468,7 @@ config MINIDUMP_MAX_ENTRIES config ICNSS tristate "Platform driver for Q6 integrated connectivity" + select CNSS_UTILS ---help--- This module adds support for Q6 integrated WLAN connectivity subsystem. This module is responsible for communicating WLAN on/off diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c index 561b9074f2ee..e0d9f68ceef9 100644 --- a/drivers/soc/qcom/glink.c +++ b/drivers/soc/qcom/glink.c @@ -30,7 +30,6 @@ #include "glink_private.h" #include "glink_xprt_if.h" -#define GLINK_CTX_CANARY 0x58544324 /* "$CTX" */ /* Number of internal IPC Logging log pages */ #define NUM_LOG_PAGES 10 #define GLINK_PM_QOS_HOLDOFF_MS 10 @@ -40,7 +39,6 @@ #define GLINK_KTHREAD_PRIO 1 -static rwlock_t magic_lock; /** * struct glink_qos_priority_bin - Packet Scheduler's priority bucket * @max_rate_kBps: Maximum rate supported by the priority bucket. @@ -316,7 +314,6 @@ struct channel_ctx { uint32_t rt_vote_on; uint32_t rt_vote_off; - uint32_t magic_number; }; static struct glink_core_if core_impl; @@ -447,33 +444,15 @@ static void glink_core_deinit_xprt_qos_cfg( static int glink_get_ch_ctx(struct channel_ctx *ctx) { - unsigned long flags; - if (!ctx) return -EINVAL; - read_lock_irqsave(&magic_lock, flags); - if (ctx->magic_number != GLINK_CTX_CANARY) { - read_unlock_irqrestore(&magic_lock, flags); - return -EINVAL; - } rwref_get(&ctx->ch_state_lhb2); - read_unlock_irqrestore(&magic_lock, flags); return 0; } -static int glink_put_ch_ctx(struct channel_ctx *ctx, bool update_magic) +static void glink_put_ch_ctx(struct channel_ctx *ctx) { - unsigned long flags; - - if (!update_magic) { - rwref_put(&ctx->ch_state_lhb2); - return 0; - } - write_lock_irqsave(&magic_lock, flags); - ctx->magic_number = 0; rwref_put(&ctx->ch_state_lhb2); - write_unlock_irqrestore(&magic_lock, flags); - return 0; } /** @@ -1935,13 +1914,14 @@ check_ctx: kfree(flcid); } + ctx->transport_ptr = xprt_ctx; + rwref_get(&ctx->ch_state_lhb2); list_add_tail(&ctx->port_list_node, &xprt_ctx->channels); GLINK_INFO_PERF_CH_XPRT(ctx, xprt_ctx, "%s: local:GLINK_CHANNEL_CLOSED\n", __func__); } - rwref_get(&ctx->ch_state_lhb2); spin_unlock_irqrestore(&xprt_ctx->xprt_ctx_lock_lhb1, flags); rwref_write_put(&xprt_ctx->xprt_state_lhb0); mutex_lock(&xprt_ctx->xprt_dbgfs_lock_lhb4); @@ -2623,7 +2603,6 @@ void *glink_open(const struct glink_open_config *cfg) ctx->notify_tx_abort = cfg->notify_tx_abort; ctx->notify_rx_tracer_pkt = cfg->notify_rx_tracer_pkt; ctx->notify_remote_rx_intent = cfg->notify_remote_rx_intent; - ctx->magic_number = GLINK_CTX_CANARY; if (!ctx->notify_rx_intent_req) ctx->notify_rx_intent_req = glink_dummy_notify_rx_intent_req; @@ -2640,7 +2619,6 @@ void *glink_open(const struct glink_open_config *cfg) ctx->local_xprt_req = best_id; ctx->no_migrate = cfg->transport && !(cfg->options & GLINK_OPT_INITIAL_XPORT); - ctx->transport_ptr = transport_ptr; ctx->local_open_state = GLINK_CHANNEL_OPENING; GLINK_INFO_PERF_CH(ctx, "%s: local:GLINK_CHANNEL_CLOSED->GLINK_CHANNEL_OPENING\n", @@ -2764,13 +2742,13 @@ int glink_close(void *handle) GLINK_INFO_CH(ctx, "%s: Closing channel, ctx: %p\n", __func__, ctx); if (ctx->local_open_state == GLINK_CHANNEL_CLOSED) { - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return 0; } if (ctx->local_open_state == GLINK_CHANNEL_CLOSING) { /* close already pending */ - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return -EBUSY; } @@ -2835,7 +2813,7 @@ relock: xprt_ctx = ctx->transport_ptr; rwref_put(&ctx->ch_state_lhb2); rwref_read_put(&xprt_ctx->xprt_state_lhb0); - glink_put_ch_ctx(ctx, true); + glink_put_ch_ctx(ctx); return ret; } EXPORT_SYMBOL(glink_close); @@ -2884,7 +2862,7 @@ static int glink_tx_common(void *handle, void *pkt_priv, struct channel_ctx *ctx = (struct channel_ctx *)handle; uint32_t riid; int ret = 0; - struct glink_core_tx_pkt *tx_info; + struct glink_core_tx_pkt *tx_info = NULL; size_t intent_size; bool is_atomic = tx_flags & (GLINK_TX_SINGLE_THREADED | GLINK_TX_ATOMIC); @@ -2899,6 +2877,13 @@ static int glink_tx_common(void *handle, void *pkt_priv, return ret; rwref_read_get_atomic(&ctx->ch_state_lhb2, is_atomic); + tx_info = kzalloc(sizeof(struct glink_core_tx_pkt), + is_atomic ? GFP_ATOMIC : GFP_KERNEL); + if (!tx_info) { + GLINK_ERR_CH(ctx, "%s: No memory for allocation\n", __func__); + ret = -ENOMEM; + goto glink_tx_common_err; + } if (!(vbuf_provider || pbuf_provider)) { ret = -EINVAL; goto glink_tx_common_err; @@ -3018,14 +3003,7 @@ static int glink_tx_common(void *handle, void *pkt_priv, GLINK_INFO_PERF_CH(ctx, "%s: R[%u]:%zu data[%p], size[%zu]. TID %u\n", __func__, riid, intent_size, data ? data : iovec, size, current->pid); - tx_info = kzalloc(sizeof(struct glink_core_tx_pkt), - is_atomic ? GFP_ATOMIC : GFP_KERNEL); - if (!tx_info) { - GLINK_ERR_CH(ctx, "%s: No memory for allocation\n", __func__); - ch_push_remote_rx_intent(ctx, intent_size, riid, cookie); - ret = -ENOMEM; - goto glink_tx_common_err; - } + rwref_lock_init(&tx_info->pkt_ref, glink_tx_pkt_release); INIT_LIST_HEAD(&tx_info->list_done); INIT_LIST_HEAD(&tx_info->list_node); @@ -3050,10 +3028,15 @@ static int glink_tx_common(void *handle, void *pkt_priv, else xprt_schedule_tx(ctx->transport_ptr, ctx, tx_info); + rwref_read_put(&ctx->ch_state_lhb2); + glink_put_ch_ctx(ctx); + return ret; + glink_tx_common_err: rwref_read_put(&ctx->ch_state_lhb2); glink_tx_common_err_2: - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); + kfree(tx_info); return ret; } @@ -3102,7 +3085,7 @@ int glink_queue_rx_intent(void *handle, const void *pkt_priv, size_t size) /* Can only queue rx intents if channel is fully opened */ GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n", __func__); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return -EBUSY; } @@ -3111,14 +3094,14 @@ int glink_queue_rx_intent(void *handle, const void *pkt_priv, size_t size) GLINK_ERR_CH(ctx, "%s: Intent pointer allocation failed size[%zu]\n", __func__, size); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return -ENOMEM; } GLINK_DBG_CH(ctx, "%s: L[%u]:%zu\n", __func__, intent_ptr->id, intent_ptr->intent_size); if (ctx->transport_ptr->capabilities & GCAP_INTENTLESS) { - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return ret; } @@ -3128,7 +3111,7 @@ int glink_queue_rx_intent(void *handle, const void *pkt_priv, size_t size) if (ret) /* unable to transmit, dequeue intent */ ch_remove_local_rx_intent(ctx, intent_ptr->id); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return ret; } EXPORT_SYMBOL(glink_queue_rx_intent); @@ -3160,12 +3143,12 @@ bool glink_rx_intent_exists(void *handle, size_t size) if (size <= intent->intent_size) { spin_unlock_irqrestore( &ctx->local_rx_intent_lst_lock_lhc1, flags); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return true; } } spin_unlock_irqrestore(&ctx->local_rx_intent_lst_lock_lhc1, flags); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return false; } EXPORT_SYMBOL(glink_rx_intent_exists); @@ -3194,7 +3177,7 @@ int glink_rx_done(void *handle, const void *ptr, bool reuse) if (IS_ERR_OR_NULL(liid_ptr)) { /* invalid pointer */ GLINK_ERR_CH(ctx, "%s: Invalid pointer %p\n", __func__, ptr); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return -EINVAL; } @@ -3220,7 +3203,7 @@ int glink_rx_done(void *handle, const void *ptr, bool reuse) /* send rx done */ ctx->transport_ptr->ops->tx_cmd_local_rx_done(ctx->transport_ptr->ops, ctx->lcid, id, reuse); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return ret; } EXPORT_SYMBOL(glink_rx_done); @@ -3274,7 +3257,7 @@ int glink_sigs_set(void *handle, uint32_t sigs) if (!ch_is_fully_opened(ctx)) { GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n", __func__); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return -EBUSY; } @@ -3284,7 +3267,7 @@ int glink_sigs_set(void *handle, uint32_t sigs) ctx->lcid, ctx->lsigs); GLINK_INFO_CH(ctx, "%s: Sent SIGNAL SET command\n", __func__); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return ret; } EXPORT_SYMBOL(glink_sigs_set); @@ -3310,12 +3293,12 @@ int glink_sigs_local_get(void *handle, uint32_t *sigs) if (!ch_is_fully_opened(ctx)) { GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n", __func__); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return -EBUSY; } *sigs = ctx->lsigs; - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return 0; } EXPORT_SYMBOL(glink_sigs_local_get); @@ -3342,12 +3325,12 @@ int glink_sigs_remote_get(void *handle, uint32_t *sigs) if (!ch_is_fully_opened(ctx)) { GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n", __func__); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return -EBUSY; } *sigs = ctx->rsigs; - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return 0; } EXPORT_SYMBOL(glink_sigs_remote_get); @@ -3452,7 +3435,7 @@ int glink_qos_latency(void *handle, unsigned long latency_us, size_t pkt_size) if (!ch_is_fully_opened(ctx)) { GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n", __func__); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return -EBUSY; } @@ -3462,7 +3445,7 @@ int glink_qos_latency(void *handle, unsigned long latency_us, size_t pkt_size) if (ret < 0) GLINK_ERR_CH(ctx, "%s: QoS %lu:%zu cannot be met\n", __func__, latency_us, pkt_size); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return ret; } EXPORT_SYMBOL(glink_qos_latency); @@ -3486,12 +3469,12 @@ int glink_qos_cancel(void *handle) if (!ch_is_fully_opened(ctx)) { GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n", __func__); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return -EBUSY; } ret = glink_qos_reset_priority(ctx); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return ret; } EXPORT_SYMBOL(glink_qos_cancel); @@ -3518,7 +3501,7 @@ int glink_qos_start(void *handle) if (!ch_is_fully_opened(ctx)) { GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n", __func__); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return -EBUSY; } @@ -3527,7 +3510,7 @@ int glink_qos_start(void *handle) ret = glink_qos_add_ch_tx_intent(ctx); spin_unlock(&ctx->tx_lists_lock_lhc3); spin_unlock_irqrestore(&ctx->transport_ptr->tx_ready_lock_lhb3, flags); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return ret; } EXPORT_SYMBOL(glink_qos_start); @@ -3555,11 +3538,11 @@ unsigned long glink_qos_get_ramp_time(void *handle, size_t pkt_size) if (!ch_is_fully_opened(ctx)) { GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n", __func__); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return (unsigned long)-EBUSY; } - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return ctx->transport_ptr->ops->get_power_vote_ramp_time( ctx->transport_ptr->ops, glink_prio_to_power_state(ctx->transport_ptr, @@ -3585,13 +3568,13 @@ int glink_start_rx_rt(void *handle) if (!ch_is_fully_opened(ctx)) { GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n", __func__); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return -EBUSY; } ret = ctx->transport_ptr->ops->rx_rt_vote(ctx->transport_ptr->ops); ctx->rt_vote_on++; GLINK_INFO_CH(ctx, "%s: Voting RX Realtime Thread %d", __func__, ret); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return ret; } @@ -3612,13 +3595,13 @@ int glink_end_rx_rt(void *handle) if (!ch_is_fully_opened(ctx)) { GLINK_ERR_CH(ctx, "%s: Channel is not fully opened\n", __func__); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return -EBUSY; } ret = ctx->transport_ptr->ops->rx_rt_unvote(ctx->transport_ptr->ops); ctx->rt_vote_off++; GLINK_INFO_CH(ctx, "%s: Unvoting RX Realtime Thread %d", __func__, ret); - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return ret; } @@ -3704,10 +3687,10 @@ int glink_wait_link_down(void *handle) if (ret) return ret; if (!ctx->transport_ptr) { - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return -EOPNOTSUPP; } - glink_put_ch_ctx(ctx, false); + glink_put_ch_ctx(ctx); return ctx->transport_ptr->ops->wait_link_down(ctx->transport_ptr->ops); } EXPORT_SYMBOL(glink_wait_link_down); @@ -4143,6 +4126,7 @@ static void glink_core_link_down(struct glink_transport_if *if_ptr) rwref_write_get(&xprt_ptr->xprt_state_lhb0); xprt_ptr->next_lcid = 1; xprt_ptr->local_state = GLINK_XPRT_DOWN; + xprt_ptr->curr_qos_rate_kBps = 0; xprt_ptr->local_version_idx = xprt_ptr->versions_entries - 1; xprt_ptr->remote_version_idx = xprt_ptr->versions_entries - 1; xprt_ptr->l_features = @@ -6248,7 +6232,6 @@ EXPORT_SYMBOL(glink_get_xprt_log_ctx); static int glink_init(void) { log_ctx = ipc_log_context_create(NUM_LOG_PAGES, "glink", 0); - rwlock_init(&magic_lock); if (!log_ctx) GLINK_ERR("%s: unable to create log context\n", __func__); glink_debugfs_init(); diff --git a/drivers/soc/qcom/glink_private.h b/drivers/soc/qcom/glink_private.h index 24053c853a83..bb794f45cff7 100644 --- a/drivers/soc/qcom/glink_private.h +++ b/drivers/soc/qcom/glink_private.h @@ -745,7 +745,6 @@ struct subsys_info { * ssr_name: Name of the subsystem recognized by the SSR framework * edge: Name of the G-Link edge * xprt: Name of the G-Link transport - * restarted: Indicates whether a restart has been triggered for this edge * cb_data: Private callback data structure for notification functions * notify_list_node: used to chain this structure in the notify list */ @@ -753,7 +752,6 @@ struct subsys_info_leaf { const char *ssr_name; const char *edge; const char *xprt; - bool restarted; struct ssr_notify_data *cb_data; struct list_head notify_list_node; }; diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c index 168db46084df..37193bbb23b7 100644 --- a/drivers/soc/qcom/glink_smem_native_xprt.c +++ b/drivers/soc/qcom/glink_smem_native_xprt.c @@ -215,7 +215,6 @@ struct edge_info { bool tx_blocked_signal_sent; struct kthread_work kwork; struct kthread_worker kworker; - struct work_struct wakeup_work; struct task_struct *task; struct tasklet_struct tasklet; struct srcu_struct use_ref; @@ -816,6 +815,32 @@ static bool get_rx_fifo(struct edge_info *einfo) } /** + * tx_wakeup_worker() - worker function to wakeup tx blocked thread + * @work: kwork associated with the edge to process commands on. + */ +static void tx_wakeup_worker(struct edge_info *einfo) +{ + bool trigger_wakeup = false; + unsigned long flags; + + if (einfo->in_ssr) + return; + if (einfo->tx_resume_needed && fifo_write_avail(einfo)) { + einfo->tx_resume_needed = false; + einfo->xprt_if.glink_core_if_ptr->tx_resume( + &einfo->xprt_if); + } + spin_lock_irqsave(&einfo->write_lock, flags); + if (waitqueue_active(&einfo->tx_blocked_queue)) { /* tx waiting ?*/ + einfo->tx_blocked_signal_sent = false; + trigger_wakeup = true; + } + spin_unlock_irqrestore(&einfo->write_lock, flags); + if (trigger_wakeup) + wake_up_all(&einfo->tx_blocked_queue); +} + +/** * __rx_worker() - process received commands on a specific edge * @einfo: Edge to process commands on. * @atomic_ctx: Indicates if the caller is in atomic context and requires any @@ -849,7 +874,7 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx) rcu_id = srcu_read_lock(&einfo->use_ref); - if (unlikely(!einfo->rx_fifo)) { + if (unlikely(!einfo->rx_fifo) && atomic_ctx) { if (!get_rx_fifo(einfo)) { srcu_read_unlock(&einfo->use_ref, rcu_id); return; @@ -865,7 +890,7 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx) if ((atomic_ctx) && ((einfo->tx_resume_needed) || (waitqueue_active(&einfo->tx_blocked_queue)))) /* tx waiting ?*/ - schedule_work(&einfo->wakeup_work); + tx_wakeup_worker(einfo); /* * Access to the fifo needs to be synchronized, however only the calls @@ -1173,39 +1198,6 @@ static void rx_worker_atomic(unsigned long param) } /** - * tx_wakeup_worker() - worker function to wakeup tx blocked thread - * @work: kwork associated with the edge to process commands on. - */ -static void tx_wakeup_worker(struct work_struct *work) -{ - struct edge_info *einfo; - bool trigger_wakeup = false; - unsigned long flags; - int rcu_id; - - einfo = container_of(work, struct edge_info, wakeup_work); - rcu_id = srcu_read_lock(&einfo->use_ref); - if (einfo->in_ssr) { - srcu_read_unlock(&einfo->use_ref, rcu_id); - return; - } - if (einfo->tx_resume_needed && fifo_write_avail(einfo)) { - einfo->tx_resume_needed = false; - einfo->xprt_if.glink_core_if_ptr->tx_resume( - &einfo->xprt_if); - } - spin_lock_irqsave(&einfo->write_lock, flags); - if (waitqueue_active(&einfo->tx_blocked_queue)) { /* tx waiting ?*/ - einfo->tx_blocked_signal_sent = false; - trigger_wakeup = true; - } - spin_unlock_irqrestore(&einfo->write_lock, flags); - if (trigger_wakeup) - wake_up_all(&einfo->tx_blocked_queue); - srcu_read_unlock(&einfo->use_ref, rcu_id); -} - -/** * rx_worker() - worker function to process received commands * @work: kwork associated with the edge to process commands on. */ @@ -2355,7 +2347,6 @@ static int glink_smem_native_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); - INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker); tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->read_from_fifo = read_from_fifo; einfo->write_to_fifo = write_to_fifo; @@ -2411,7 +2402,7 @@ static int glink_smem_native_probe(struct platform_device *pdev) einfo->tx_fifo = smem_alloc(SMEM_GLINK_NATIVE_XPRT_FIFO_0, einfo->tx_fifo_size, einfo->remote_proc_id, - SMEM_ITEM_CACHED_FLAG); + 0); if (!einfo->tx_fifo) { pr_err("%s: smem alloc of tx fifo failed\n", __func__); rc = -ENOMEM; @@ -2457,7 +2448,6 @@ request_irq_fail: reg_xprt_fail: smem_alloc_fail: flush_kthread_worker(&einfo->kworker); - flush_work(&einfo->wakeup_work); kthread_stop(einfo->task); einfo->task = NULL; tasklet_kill(&einfo->tasklet); @@ -2546,7 +2536,6 @@ static int glink_rpm_native_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); - INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker); tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->intentless = true; einfo->read_from_fifo = memcpy32_fromio; @@ -2707,7 +2696,6 @@ request_irq_fail: reg_xprt_fail: toc_init_fail: flush_kthread_worker(&einfo->kworker); - flush_work(&einfo->wakeup_work); kthread_stop(einfo->task); einfo->task = NULL; tasklet_kill(&einfo->tasklet); @@ -2839,7 +2827,6 @@ static int glink_mailbox_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); - INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker); tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->read_from_fifo = read_from_fifo; einfo->write_to_fifo = write_to_fifo; @@ -2960,7 +2947,6 @@ request_irq_fail: reg_xprt_fail: smem_alloc_fail: flush_kthread_worker(&einfo->kworker); - flush_work(&einfo->wakeup_work); kthread_stop(einfo->task); einfo->task = NULL; tasklet_kill(&einfo->tasklet); diff --git a/drivers/soc/qcom/glink_ssr.c b/drivers/soc/qcom/glink_ssr.c index 7e23b0bc3852..c28eeab92fed 100644 --- a/drivers/soc/qcom/glink_ssr.c +++ b/drivers/soc/qcom/glink_ssr.c @@ -22,7 +22,6 @@ #include <linux/random.h> #include <soc/qcom/glink.h> #include <soc/qcom/subsystem_notif.h> -#include <soc/qcom/subsystem_restart.h> #include "glink_private.h" #define GLINK_SSR_REPLY_TIMEOUT HZ @@ -608,13 +607,9 @@ int notify_for_subsystem(struct subsys_info *ss_info) kfree(do_cleanup_data); ss_leaf_entry->cb_data->do_cleanup_data = NULL; - if (strcmp(ss_leaf_entry->ssr_name, "rpm")) { - subsystem_restart(ss_leaf_entry->ssr_name); - ss_leaf_entry->restarted = true; - } else { + if (!strcmp(ss_leaf_entry->ssr_name, "rpm")) panic("%s: Could not queue intent for RPM!\n", __func__); - } atomic_dec(&responses_remaining); kref_put(&ss_leaf_entry->cb_data->cb_kref, cb_data_release); @@ -639,13 +634,9 @@ int notify_for_subsystem(struct subsys_info *ss_info) kfree(do_cleanup_data); ss_leaf_entry->cb_data->do_cleanup_data = NULL; - if (strcmp(ss_leaf_entry->ssr_name, "rpm")) { - subsystem_restart(ss_leaf_entry->ssr_name); - ss_leaf_entry->restarted = true; - } else { + if (!strcmp(ss_leaf_entry->ssr_name, "rpm")) panic("%s: glink_tx() to RPM failed!\n", __func__); - } atomic_dec(&responses_remaining); kref_put(&ss_leaf_entry->cb_data->cb_kref, cb_data_release); @@ -687,11 +678,7 @@ int notify_for_subsystem(struct subsys_info *ss_info) /* Check for RPM, as it can't be restarted */ if (!strcmp(ss_leaf_entry->ssr_name, "rpm")) panic("%s: RPM failed to respond!\n", __func__); - else if (!ss_leaf_entry->restarted) - subsystem_restart(ss_leaf_entry->ssr_name); } - ss_leaf_entry->restarted = false; - if (!IS_ERR_OR_NULL(ss_leaf_entry->cb_data)) ss_leaf_entry->cb_data->responded = false; kref_put(&ss_leaf_entry->cb_data->cb_kref, cb_data_release); @@ -1011,7 +998,6 @@ static int glink_ssr_probe(struct platform_device *pdev) ss_info_leaf->ssr_name = subsys_name; ss_info_leaf->edge = edge; ss_info_leaf->xprt = xprt; - ss_info_leaf->restarted = false; list_add_tail(&ss_info_leaf->notify_list_node, &ss_info->notify_list); ss_info->notify_list_len++; diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 43d954a0f7c7..21be894414bc 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -168,6 +168,76 @@ enum icnss_driver_event_type { ICNSS_DRIVER_EVENT_MAX, }; +enum icnss_msa_perm { + ICNSS_MSA_PERM_HLOS_ALL = 0, + ICNSS_MSA_PERM_WLAN_HW_RW = 1, + ICNSS_MSA_PERM_DUMP_COLLECT = 2, + ICNSS_MSA_PERM_MAX, +}; + +#define ICNSS_MAX_VMIDS 4 + +struct icnss_mem_region_info { + uint64_t reg_addr; + uint32_t size; + uint8_t secure_flag; + enum icnss_msa_perm perm; +}; + +struct icnss_msa_perm_list_t { + int vmids[ICNSS_MAX_VMIDS]; + int perms[ICNSS_MAX_VMIDS]; + int nelems; +}; + +struct icnss_msa_perm_list_t msa_perm_secure_list[ICNSS_MSA_PERM_MAX] = { + [ICNSS_MSA_PERM_HLOS_ALL] = { + .vmids = {VMID_HLOS}, + .perms = {PERM_READ | PERM_WRITE | PERM_EXEC}, + .nelems = 1, + }, + + [ICNSS_MSA_PERM_WLAN_HW_RW] = { + .vmids = {VMID_MSS_MSA, VMID_WLAN}, + .perms = {PERM_READ | PERM_WRITE, + PERM_READ | PERM_WRITE}, + .nelems = 2, + }, + + [ICNSS_MSA_PERM_DUMP_COLLECT] = { + .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_HLOS}, + .perms = {PERM_READ | PERM_WRITE, + PERM_READ | PERM_WRITE, + PERM_READ}, + .nelems = 3, + }, +}; + +struct icnss_msa_perm_list_t msa_perm_list[ICNSS_MSA_PERM_MAX] = { + [ICNSS_MSA_PERM_HLOS_ALL] = { + .vmids = {VMID_HLOS}, + .perms = {PERM_READ | PERM_WRITE | PERM_EXEC}, + .nelems = 1, + }, + + [ICNSS_MSA_PERM_WLAN_HW_RW] = { + .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE}, + .perms = {PERM_READ | PERM_WRITE, + PERM_READ | PERM_WRITE, + PERM_READ | PERM_WRITE}, + .nelems = 3, + }, + + [ICNSS_MSA_PERM_DUMP_COLLECT] = { + .vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE, VMID_HLOS}, + .perms = {PERM_READ | PERM_WRITE, + PERM_READ | PERM_WRITE, + PERM_READ | PERM_WRITE, + PERM_READ}, + .nelems = 4, + }, +}; + struct icnss_event_pd_service_down_data { bool crashed; bool fw_rejuvenate; @@ -191,13 +261,14 @@ enum icnss_driver_state { ICNSS_FW_TEST_MODE, ICNSS_PM_SUSPEND, ICNSS_PM_SUSPEND_NOIRQ, - ICNSS_SSR_ENABLED, - ICNSS_PDR_ENABLED, + ICNSS_SSR_REGISTERED, + ICNSS_PDR_REGISTERED, ICNSS_PD_RESTART, ICNSS_MSA0_ASSIGNED, ICNSS_WLFW_EXISTS, ICNSS_WDOG_BITE, ICNSS_SHUTDOWN_DONE, + ICNSS_HOST_TRIGGERED_PDR, }; struct ce_irq_list { @@ -250,6 +321,13 @@ struct icnss_stats { uint32_t disable; } ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS]; + struct { + uint32_t pdr_fw_crash; + uint32_t pdr_host_error; + uint32_t root_pd_crash; + uint32_t root_pd_shutdown; + } recovery; + uint32_t pm_suspend; uint32_t pm_suspend_err; uint32_t pm_resume; @@ -290,7 +368,6 @@ struct icnss_stats { uint32_t rejuvenate_ack_req; uint32_t rejuvenate_ack_resp; uint32_t rejuvenate_ack_err; - uint32_t trigger_recovery; }; #define MAX_NO_OF_MAC_ADDR 4 @@ -299,6 +376,20 @@ struct icnss_wlan_mac_addr { uint32_t no_of_mac_addr_set; }; +enum icnss_pdr_cause_index { + ICNSS_FW_CRASH, + ICNSS_ROOT_PD_CRASH, + ICNSS_ROOT_PD_SHUTDOWN, + ICNSS_HOST_ERROR, +}; + +static const char * const icnss_pdr_cause[] = { + [ICNSS_FW_CRASH] = "FW crash", + [ICNSS_ROOT_PD_CRASH] = "Root PD crashed", + [ICNSS_ROOT_PD_SHUTDOWN] = "Root PD shutdown", + [ICNSS_HOST_ERROR] = "Host error", +}; + struct service_notifier_context { void *handle; uint32_t instance_id; @@ -364,10 +455,9 @@ static struct icnss_priv { bool is_wlan_mac_set; struct icnss_wlan_mac_addr wlan_mac_addr; bool bypass_s1_smmu; + struct mutex dev_lock; } *penv; -static enum cnss_cc_src cnss_cc_source = CNSS_SOURCE_CORE; - #ifdef CONFIG_ICNSS_DEBUG static void icnss_ignore_qmi_timeout(bool ignore) { @@ -377,6 +467,84 @@ static void icnss_ignore_qmi_timeout(bool ignore) static void icnss_ignore_qmi_timeout(bool ignore) { } #endif +static int icnss_assign_msa_perm(struct icnss_mem_region_info + *mem_region, enum icnss_msa_perm new_perm) +{ + int ret = 0; + phys_addr_t addr; + u32 size; + u32 i = 0; + u32 source_vmids[ICNSS_MAX_VMIDS]; + u32 source_nelems; + u32 dest_vmids[ICNSS_MAX_VMIDS]; + u32 dest_perms[ICNSS_MAX_VMIDS]; + u32 dest_nelems; + enum icnss_msa_perm cur_perm = mem_region->perm; + struct icnss_msa_perm_list_t *new_perm_list, *old_perm_list; + + addr = mem_region->reg_addr; + size = mem_region->size; + + if (mem_region->secure_flag) { + new_perm_list = &msa_perm_secure_list[new_perm]; + old_perm_list = &msa_perm_secure_list[cur_perm]; + } else { + new_perm_list = &msa_perm_list[new_perm]; + old_perm_list = &msa_perm_list[cur_perm]; + } + + source_nelems = old_perm_list->nelems; + dest_nelems = new_perm_list->nelems; + + for (i = 0; i < source_nelems; ++i) + source_vmids[i] = old_perm_list->vmids[i]; + + for (i = 0; i < dest_nelems; ++i) { + dest_vmids[i] = new_perm_list->vmids[i]; + dest_perms[i] = new_perm_list->perms[i]; + } + + ret = hyp_assign_phys(addr, size, source_vmids, source_nelems, + dest_vmids, dest_perms, dest_nelems); + if (ret) { + icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n", + &addr, size, ret); + goto out; + } + + icnss_pr_dbg("Hypervisor map for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x," + "source[3]=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x, dest[3]=%x\n", + source_nelems, source_vmids[0], source_vmids[1], + source_vmids[2], source_vmids[3], dest_nelems, + dest_vmids[0], dest_vmids[1], dest_vmids[2], + dest_vmids[3]); +out: + return ret; +} + +static int icnss_assign_msa_perm_all(struct icnss_priv *priv, + enum icnss_msa_perm new_perm) +{ + int ret; + int i; + enum icnss_msa_perm old_perm; + + for (i = 0; i < priv->nr_mem_region; i++) { + old_perm = priv->mem_region[i].perm; + ret = icnss_assign_msa_perm(&priv->mem_region[i], new_perm); + if (ret) + goto err_unmap; + priv->mem_region[i].perm = new_perm; + } + return 0; + +err_unmap: + for (i--; i >= 0; i--) { + icnss_assign_msa_perm(&priv->mem_region[i], old_perm); + } + return ret; +} + static void icnss_pm_stay_awake(struct icnss_priv *priv) { if (atomic_inc_return(&priv->pm_count) != 1) @@ -941,18 +1109,6 @@ static int icnss_hw_power_off(struct icnss_priv *priv) return ret; } -void cnss_set_cc_source(enum cnss_cc_src cc_source) -{ - cnss_cc_source = cc_source; -} -EXPORT_SYMBOL(cnss_set_cc_source); - -enum cnss_cc_src cnss_get_cc_source(void) -{ - return cnss_cc_source; -} -EXPORT_SYMBOL(cnss_get_cc_source); - int icnss_power_on(struct device *dev) { struct icnss_priv *priv = dev_get_drvdata(dev); @@ -994,119 +1150,6 @@ int icnss_power_off(struct device *dev) } EXPORT_SYMBOL(icnss_power_off); -static int icnss_map_msa_permissions(struct icnss_mem_region_info *mem_region) -{ - int ret = 0; - phys_addr_t addr; - u32 size; - u32 source_vmlist[1] = {VMID_HLOS}; - int dest_vmids[3] = {VMID_MSS_MSA, VMID_WLAN, 0}; - int dest_perms[3] = {PERM_READ|PERM_WRITE, - PERM_READ|PERM_WRITE, - PERM_READ|PERM_WRITE}; - int source_nelems = sizeof(source_vmlist)/sizeof(u32); - int dest_nelems = 0; - - addr = mem_region->reg_addr; - size = mem_region->size; - - if (!mem_region->secure_flag) { - dest_vmids[2] = VMID_WLAN_CE; - dest_nelems = 3; - } else { - dest_vmids[2] = 0; - dest_nelems = 2; - } - ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems, - dest_vmids, dest_perms, dest_nelems); - if (ret) { - icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n", - &addr, size, ret); - goto out; - } - - icnss_pr_dbg("Hypervisor map for source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n", - source_vmlist[0], dest_nelems, dest_vmids[0], - dest_vmids[1], dest_vmids[2]); -out: - return ret; - -} - -static int icnss_unmap_msa_permissions(struct icnss_mem_region_info *mem_region) -{ - int ret = 0; - phys_addr_t addr; - u32 size; - u32 dest_vmids[1] = {VMID_HLOS}; - int source_vmlist[3] = {VMID_MSS_MSA, VMID_WLAN, 0}; - int dest_perms[1] = {PERM_READ|PERM_WRITE|PERM_EXEC}; - int source_nelems = 0; - int dest_nelems = sizeof(dest_vmids)/sizeof(u32); - - addr = mem_region->reg_addr; - size = mem_region->size; - - if (!mem_region->secure_flag) { - source_vmlist[2] = VMID_WLAN_CE; - source_nelems = 3; - } else { - source_vmlist[2] = 0; - source_nelems = 2; - } - - ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems, - dest_vmids, dest_perms, dest_nelems); - if (ret) { - icnss_pr_err("Hyperviser unmap failed for PA=%pa size=%u err=%d\n", - &addr, size, ret); - goto out; - } - icnss_pr_dbg("Hypervisor unmap for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n", - source_nelems, source_vmlist[0], source_vmlist[1], - source_vmlist[2], dest_vmids[0]); -out: - return ret; -} - -static int icnss_setup_msa_permissions(struct icnss_priv *priv) -{ - int ret; - int i; - - if (test_bit(ICNSS_MSA0_ASSIGNED, &priv->state)) - return 0; - - for (i = 0; i < priv->nr_mem_region; i++) { - - ret = icnss_map_msa_permissions(&priv->mem_region[i]); - if (ret) - goto err_unmap; - } - - set_bit(ICNSS_MSA0_ASSIGNED, &priv->state); - - return 0; - -err_unmap: - for (i--; i >= 0; i--) - icnss_unmap_msa_permissions(&priv->mem_region[i]); - return ret; -} - -static void icnss_remove_msa_permissions(struct icnss_priv *priv) -{ - int i; - - if (!test_bit(ICNSS_MSA0_ASSIGNED, &priv->state)) - return; - - for (i = 0; i < priv->nr_mem_region; i++) - icnss_unmap_msa_permissions(&priv->mem_region[i]); - - clear_bit(ICNSS_MSA0_ASSIGNED, &priv->state); -} - static int wlfw_msa_mem_info_send_sync_msg(void) { int ret; @@ -1912,9 +1955,12 @@ static int icnss_driver_event_server_arrive(void *data) if (ret < 0) goto err_power_on; - ret = icnss_setup_msa_permissions(penv); - if (ret < 0) - goto err_power_on; + if (!test_bit(ICNSS_MSA0_ASSIGNED, &penv->state)) { + ret = icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_WLAN_HW_RW); + if (ret < 0) + goto err_power_on; + set_bit(ICNSS_MSA0_ASSIGNED, &penv->state); + } ret = wlfw_msa_ready_send_sync_msg(); if (ret < 0) @@ -1932,7 +1978,7 @@ static int icnss_driver_event_server_arrive(void *data) return ret; err_setup_msa: - icnss_remove_msa_permissions(penv); + icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL); err_power_on: icnss_hw_power_off(penv); fail: @@ -2347,26 +2393,39 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, struct icnss_priv *priv = container_of(nb, struct icnss_priv, modem_ssr_nb); struct icnss_uevent_fw_down_data fw_down_data; + int ret = 0; icnss_pr_vdbg("Modem-Notify: event %lu\n", code); - if (code == SUBSYS_AFTER_SHUTDOWN && - notif->crashed == CRASH_STATUS_ERR_FATAL) { - icnss_remove_msa_permissions(priv); - icnss_pr_info("Collecting msa0 segment dump\n"); - icnss_msa0_ramdump(priv); + if (code == SUBSYS_AFTER_SHUTDOWN) { + ret = icnss_assign_msa_perm_all(priv, + ICNSS_MSA_PERM_DUMP_COLLECT); + if (!ret) { + icnss_pr_info("Collecting msa0 segment dump\n"); + icnss_msa0_ramdump(priv); + icnss_assign_msa_perm_all(priv, + ICNSS_MSA_PERM_WLAN_HW_RW); + } else { + icnss_pr_err("Not able to Collect msa0 segment dump" + "Apps permissions not assigned %d\n", ret); + } return NOTIFY_OK; } if (code != SUBSYS_BEFORE_SHUTDOWN) return NOTIFY_OK; - if (test_bit(ICNSS_PDR_ENABLED, &priv->state)) + if (test_bit(ICNSS_PDR_REGISTERED, &priv->state)) return NOTIFY_OK; icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n", priv->state, notif->crashed); + if (notif->crashed) + priv->stats.recovery.root_pd_crash++; + else + priv->stats.recovery.root_pd_shutdown++; + icnss_ignore_qmi_timeout(true); event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); @@ -2402,14 +2461,14 @@ static int icnss_modem_ssr_register_notifier(struct icnss_priv *priv) icnss_pr_err("Modem register notifier failed: %d\n", ret); } - set_bit(ICNSS_SSR_ENABLED, &priv->state); + set_bit(ICNSS_SSR_REGISTERED, &priv->state); return ret; } static int icnss_modem_ssr_unregister_notifier(struct icnss_priv *priv) { - if (!test_and_clear_bit(ICNSS_SSR_ENABLED, &priv->state)) + if (!test_and_clear_bit(ICNSS_SSR_REGISTERED, &priv->state)) return 0; subsys_notif_unregister_notifier(priv->modem_notify_handler, @@ -2423,7 +2482,7 @@ static int icnss_pdr_unregister_notifier(struct icnss_priv *priv) { int i; - if (!test_and_clear_bit(ICNSS_PDR_ENABLED, &priv->state)) + if (!test_and_clear_bit(ICNSS_PDR_REGISTERED, &priv->state)) return 0; for (i = 0; i < priv->total_domains; i++) @@ -2446,6 +2505,7 @@ static int icnss_service_notifier_notify(struct notifier_block *nb, enum pd_subsys_state *state = data; struct icnss_event_pd_service_down_data *event_data; struct icnss_uevent_fw_down_data fw_down_data; + enum icnss_pdr_cause_index cause = ICNSS_ROOT_PD_CRASH; icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n", notification, priv->state); @@ -2460,26 +2520,40 @@ static int icnss_service_notifier_notify(struct notifier_block *nb, if (state == NULL) { event_data->crashed = true; + priv->stats.recovery.root_pd_crash++; goto event_post; } - icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx\n", - *state, priv->state); - switch (*state) { case ROOT_PD_WDOG_BITE: event_data->crashed = true; event_data->wdog_bite = true; + priv->stats.recovery.root_pd_crash++; break; case ROOT_PD_SHUTDOWN: + cause = ICNSS_ROOT_PD_SHUTDOWN; + priv->stats.recovery.root_pd_shutdown++; + break; + case USER_PD_STATE_CHANGE: + if (test_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state)) { + cause = ICNSS_HOST_ERROR; + priv->stats.recovery.pdr_host_error++; + } else { + cause = ICNSS_FW_CRASH; + priv->stats.recovery.pdr_fw_crash++; + } break; default: event_data->crashed = true; + priv->stats.recovery.root_pd_crash++; break; } + icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n", + *state, priv->state, icnss_pdr_cause[cause]); event_post: icnss_ignore_qmi_timeout(true); + clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state); fw_down_data.crashed = event_data->crashed; icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data); @@ -2547,9 +2621,10 @@ static int icnss_get_service_location_notify(struct notifier_block *nb, priv->service_notifier = notifier; priv->total_domains = pd->total_domains; - set_bit(ICNSS_PDR_ENABLED, &priv->state); + set_bit(ICNSS_PDR_REGISTERED, &priv->state); - icnss_pr_dbg("PD restart enabled, state: 0x%lx\n", priv->state); + icnss_pr_dbg("PD notification registration happened, state: 0x%lx\n", + priv->state); return NOTIFY_OK; @@ -3204,7 +3279,7 @@ int icnss_trigger_recovery(struct device *dev) goto out; } - if (!test_bit(ICNSS_PDR_ENABLED, &priv->state)) { + if (!test_bit(ICNSS_PDR_REGISTERED, &priv->state)) { icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n", priv->state); ret = -EOPNOTSUPP; @@ -3221,7 +3296,6 @@ int icnss_trigger_recovery(struct device *dev) WARN_ON(1); icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n", priv->state); - priv->stats.trigger_recovery++; /* * Initiate PDR, required only for the first instance @@ -3229,6 +3303,9 @@ int icnss_trigger_recovery(struct device *dev) ret = service_notif_pd_restart(priv->service_notifier[0].name, priv->service_notifier[0].instance_id); + if (!ret) + set_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state); + out: return ret; } @@ -3583,9 +3660,6 @@ static ssize_t icnss_fw_debug_write(struct file *fp, if (ret) return ret; - if (ret == 0) - memset(&priv->stats, 0, sizeof(priv->stats)); - return count; } @@ -3658,11 +3732,11 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) case ICNSS_PM_SUSPEND_NOIRQ: seq_puts(s, "PM SUSPEND NOIRQ"); continue; - case ICNSS_SSR_ENABLED: - seq_puts(s, "SSR ENABLED"); + case ICNSS_SSR_REGISTERED: + seq_puts(s, "SSR REGISTERED"); continue; - case ICNSS_PDR_ENABLED: - seq_puts(s, "PDR ENABLED"); + case ICNSS_PDR_REGISTERED: + seq_puts(s, "PDR REGISTERED"); continue; case ICNSS_PD_RESTART: seq_puts(s, "PD RESTART"); @@ -3679,6 +3753,9 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) case ICNSS_SHUTDOWN_DONE: seq_puts(s, "SHUTDOWN DONE"); continue; + case ICNSS_HOST_TRIGGERED_PDR: + seq_puts(s, "HOST TRIGGERED PDR"); + continue; } seq_printf(s, "UNKNOWN-%d", i); @@ -3777,7 +3854,10 @@ static int icnss_stats_show(struct seq_file *s, void *data) ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req); ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp); ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err); - ICNSS_STATS_DUMP(s, priv, trigger_recovery); + ICNSS_STATS_DUMP(s, priv, recovery.pdr_fw_crash); + ICNSS_STATS_DUMP(s, priv, recovery.pdr_host_error); + ICNSS_STATS_DUMP(s, priv, recovery.root_pd_crash); + ICNSS_STATS_DUMP(s, priv, recovery.root_pd_shutdown); seq_puts(s, "\n<------------------ PM stats ------------------->\n"); ICNSS_STATS_DUMP(s, priv, pm_suspend); @@ -3906,12 +3986,14 @@ static int icnss_regread_show(struct seq_file *s, void *data) { struct icnss_priv *priv = s->private; + mutex_lock(&priv->dev_lock); if (!priv->diag_reg_read_buf) { seq_puts(s, "Usage: echo <mem_type> <offset> <data_len> > <debugfs>/icnss/reg_read\n"); if (!test_bit(ICNSS_FW_READY, &priv->state)) seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n"); + mutex_unlock(&priv->dev_lock); return 0; } @@ -3925,6 +4007,7 @@ static int icnss_regread_show(struct seq_file *s, void *data) priv->diag_reg_read_len = 0; kfree(priv->diag_reg_read_buf); priv->diag_reg_read_buf = NULL; + mutex_unlock(&priv->dev_lock); return 0; } @@ -3985,18 +4068,22 @@ static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf, data_len > QMI_WLFW_MAX_DATA_SIZE_V01) return -EINVAL; + mutex_lock(&priv->dev_lock); kfree(priv->diag_reg_read_buf); priv->diag_reg_read_buf = NULL; reg_buf = kzalloc(data_len, GFP_KERNEL); - if (!reg_buf) + if (!reg_buf) { + mutex_unlock(&priv->dev_lock); return -ENOMEM; + } ret = wlfw_athdiag_read_send_sync_msg(priv, reg_offset, mem_type, data_len, reg_buf); if (ret) { kfree(reg_buf); + mutex_unlock(&priv->dev_lock); return ret; } @@ -4004,6 +4091,7 @@ static ssize_t icnss_regread_write(struct file *fp, const char __user *user_buf, priv->diag_reg_read_mem_type = mem_type; priv->diag_reg_read_len = data_len; priv->diag_reg_read_buf = reg_buf; + mutex_unlock(&priv->dev_lock); return count; } @@ -4251,6 +4339,7 @@ static int icnss_probe(struct platform_device *pdev) spin_lock_init(&priv->event_lock); spin_lock_init(&priv->on_off_lock); + mutex_init(&priv->dev_lock); priv->event_wq = alloc_workqueue("icnss_driver_event", WQ_UNBOUND, 1); if (!priv->event_wq) { @@ -4276,6 +4365,11 @@ static int icnss_probe(struct platform_device *pdev) icnss_debugfs_create(priv); + ret = device_init_wakeup(&priv->pdev->dev, true); + if (ret) + icnss_pr_err("Failed to init platform device wakeup source, err = %d\n", + ret); + penv = priv; icnss_pr_info("Platform driver probed successfully\n"); @@ -4296,6 +4390,8 @@ static int icnss_remove(struct platform_device *pdev) { icnss_pr_info("Removing driver: state: 0x%lx\n", penv->state); + device_init_wakeup(&penv->pdev->dev, false); + icnss_debugfs_destroy(penv); icnss_modem_ssr_unregister_notifier(penv); @@ -4313,7 +4409,8 @@ static int icnss_remove(struct platform_device *pdev) icnss_hw_power_off(penv); - icnss_remove_msa_permissions(penv); + icnss_assign_msa_perm_all(penv, ICNSS_MSA_PERM_HLOS_ALL); + clear_bit(ICNSS_MSA0_ASSIGNED, &penv->state); dev_set_drvdata(&pdev->dev, NULL); diff --git a/drivers/soc/qcom/icnss_utils.c b/drivers/soc/qcom/icnss_utils.c index a7a0ffa2c18e..ca22b6cdf4a1 100644 --- a/drivers/soc/qcom/icnss_utils.c +++ b/drivers/soc/qcom/icnss_utils.c @@ -12,11 +12,14 @@ #include <linux/module.h> #include <linux/slab.h> +#include <soc/qcom/icnss.h> #define ICNSS_MAX_CH_NUM 45 static DEFINE_MUTEX(unsafe_channel_list_lock); static DEFINE_SPINLOCK(dfs_nol_info_lock); +static int driver_load_cnt; +static enum cnss_cc_src icnss_cc_source = CNSS_SOURCE_CORE; static struct icnss_unsafe_channel_list { u16 unsafe_ch_count; @@ -124,3 +127,28 @@ int icnss_wlan_get_dfs_nol(void *info, u16 info_len) return len; } EXPORT_SYMBOL(icnss_wlan_get_dfs_nol); + +void icnss_increment_driver_load_cnt(void) +{ + ++driver_load_cnt; +} +EXPORT_SYMBOL(icnss_increment_driver_load_cnt); + +int icnss_get_driver_load_cnt(void) +{ + return driver_load_cnt; +} +EXPORT_SYMBOL(icnss_get_driver_load_cnt); + + +void icnss_set_cc_source(enum cnss_cc_src cc_source) +{ + icnss_cc_source = cc_source; +} +EXPORT_SYMBOL(icnss_set_cc_source); + +enum cnss_cc_src icnss_get_cc_source(void) +{ + return icnss_cc_source; +} +EXPORT_SYMBOL(icnss_get_cc_source); diff --git a/drivers/soc/qcom/ipc_router_glink_xprt.c b/drivers/soc/qcom/ipc_router_glink_xprt.c index 1f36dd0ba07e..7dd1683881fb 100644 --- a/drivers/soc/qcom/ipc_router_glink_xprt.c +++ b/drivers/soc/qcom/ipc_router_glink_xprt.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -70,6 +70,7 @@ if (ipc_router_glink_xprt_debug_mask) \ * @xprt_version: IPC Router header version supported by this XPRT. * @xprt_option: XPRT specific options to be handled by IPC Router. * @disable_pil_loading: Disable PIL Loading of the subsystem. + * @dynamic_wakeup_source: Dynamic wakeup source for this subsystem. */ struct ipc_router_glink_xprt { struct list_head list; @@ -91,6 +92,7 @@ struct ipc_router_glink_xprt { uint32_t cur_lo_intents_cnt; uint32_t cur_md_intents_cnt; uint32_t cur_hi_intents_cnt; + bool dynamic_wakeup_source; }; struct ipc_router_glink_xprt_work { @@ -127,6 +129,7 @@ static void glink_xprt_close_event(struct work_struct *work); * @link_id: Network Cluster ID to which this XPRT belongs to. * @xprt_version: IPC Router header version supported by this XPRT. * @disable_pil_loading:Disable PIL Loading of the subsystem. + * @dynamic_wakeup_source: Dynamic wakeup source for this subsystem. */ struct ipc_router_glink_xprt_config { char ch_name[GLINK_NAME_SIZE]; @@ -138,6 +141,7 @@ struct ipc_router_glink_xprt_config { unsigned xprt_version; unsigned xprt_option; bool disable_pil_loading; + bool dynamic_wakeup_source; }; #define MODULE_NAME "ipc_router_glink_xprt" @@ -292,6 +296,14 @@ static void glink_xprt_sft_close_done(struct msm_ipc_router_xprt *xprt) complete_all(&glink_xprtp->sft_close_complete); } +static bool ipc_router_glink_xprt_get_ws_info(struct msm_ipc_router_xprt *xprt) +{ + struct ipc_router_glink_xprt *glink_xprtp = + container_of(xprt, struct ipc_router_glink_xprt, xprt); + + return glink_xprtp->dynamic_wakeup_source; +} + static struct rr_packet *glink_xprt_copy_data(struct read_work *rx_work) { void *buf, *pbuf, *dest_buf; @@ -705,6 +717,8 @@ static int ipc_router_glink_config_init( glink_xprtp->xprt_option = glink_xprt_config->xprt_option; glink_xprtp->disable_pil_loading = glink_xprt_config->disable_pil_loading; + glink_xprtp->dynamic_wakeup_source = + glink_xprt_config->dynamic_wakeup_source; if (!glink_xprtp->disable_pil_loading) strlcpy(glink_xprtp->pil_edge, glink_xprt_config->pil_edge, @@ -727,6 +741,7 @@ static int ipc_router_glink_config_init( glink_xprtp->xprt.write = ipc_router_glink_xprt_write; glink_xprtp->xprt.close = ipc_router_glink_xprt_close; glink_xprtp->xprt.sft_close_done = glink_xprt_sft_close_done; + glink_xprtp->xprt.get_ws_info = ipc_router_glink_xprt_get_ws_info; glink_xprtp->xprt.priv = NULL; init_rwsem(&glink_xprtp->ss_reset_rwlock); @@ -821,6 +836,10 @@ static int parse_devicetree(struct device_node *node, scnprintf(glink_xprt_config->ipc_rtr_xprt_name, IPC_RTR_XPRT_NAME_LEN, "%s_%s", edge, ch_name); + key = "qcom,dynamic-wakeup-source"; + glink_xprt_config->dynamic_wakeup_source = + of_property_read_bool(node, key); + return 0; error: diff --git a/drivers/soc/qcom/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c index 7406dba44320..6dd4b06bf377 100644 --- a/drivers/soc/qcom/memshare/msm_memshare.c +++ b/drivers/soc/qcom/memshare/msm_memshare.c @@ -968,8 +968,8 @@ static int memshare_child_probe(struct platform_device *pdev) /* * Memshare allocation for guaranteed clients */ - if (memblock[num_clients].guarantee) { - if (client_id == 1 && size > 0) + if (memblock[num_clients].guarantee && size > 0) { + if (client_id == 1) size += MEMSHARE_GUARD_BYTES; rc = memshare_alloc(memsh_child->dev, size, @@ -980,6 +980,7 @@ static int memshare_child_probe(struct platform_device *pdev) return rc; } memblock[num_clients].alloted = 1; + shared_hyp_mapping(num_clients); } /* diff --git a/drivers/soc/qcom/msm_bus/msm_bus_rules.c b/drivers/soc/qcom/msm_bus/msm_bus_rules.c index ea29e303bbde..43a892bbd27c 100644 --- a/drivers/soc/qcom/msm_bus/msm_bus_rules.c +++ b/drivers/soc/qcom/msm_bus/msm_bus_rules.c @@ -597,7 +597,7 @@ void msm_rule_register(int num_rules, struct bus_rule_type *rule, static bool __rule_unregister(int num_rules, struct bus_rule_type *rule, struct notifier_block *nb) { - int i; + int i = 0; struct rule_node_info *node = NULL; struct rule_node_info *node_tmp = NULL; struct rules_def *node_rule; diff --git a/drivers/soc/qcom/msm_glink_pkt.c b/drivers/soc/qcom/msm_glink_pkt.c index a92e5c416678..0a80016f1942 100644 --- a/drivers/soc/qcom/msm_glink_pkt.c +++ b/drivers/soc/qcom/msm_glink_pkt.c @@ -33,7 +33,8 @@ #include <linux/termios.h> #include <soc/qcom/glink.h> - +/* This Limit ensures that auto queue will not exhaust memory on remote side */ +#define MAX_PENDING_GLINK_PKT 5 #define MODULE_NAME "msm_glinkpkt" #define DEVICE_NAME "glinkpkt" #define WAKEUPSOURCE_TIMEOUT (2000) /* two seconds */ @@ -136,6 +137,7 @@ struct glink_pkt_dev { struct glink_link_info link_info; void *link_state_handle; bool link_up; + bool auto_intent_enabled; }; /** @@ -447,8 +449,26 @@ void glink_pkt_notify_state(void *handle, const void *priv, unsigned event) bool glink_pkt_rmt_rx_intent_req_cb(void *handle, const void *priv, size_t sz) { struct queue_rx_intent_work *work_item; + int pending_pkt_count = 0; + struct glink_rx_pkt *pkt = NULL; + unsigned long flags; + struct glink_pkt_dev *devp = (struct glink_pkt_dev *)priv; + GLINK_PKT_INFO("%s(): QUEUE RX INTENT to receive size[%zu]\n", __func__, sz); + if (devp->auto_intent_enabled) { + spin_lock_irqsave(&devp->pkt_list_lock, flags); + list_for_each_entry(pkt, &devp->pkt_list, list) + pending_pkt_count++; + spin_unlock_irqrestore(&devp->pkt_list_lock, flags); + if (pending_pkt_count > MAX_PENDING_GLINK_PKT) { + GLINK_PKT_ERR("%s failed, max limit reached\n", + __func__); + return false; + } + } else { + return false; + } work_item = kzalloc(sizeof(*work_item), GFP_ATOMIC); if (!work_item) { @@ -457,7 +477,7 @@ bool glink_pkt_rmt_rx_intent_req_cb(void *handle, const void *priv, size_t sz) } work_item->intent_size = sz; - work_item->devp = (struct glink_pkt_dev *)priv; + work_item->devp = devp; INIT_WORK(&work_item->work, glink_pkt_queue_rx_intent_worker); queue_work(glink_pkt_wq, &work_item->work); @@ -626,10 +646,11 @@ ssize_t glink_pkt_read(struct file *file, } mutex_lock(&devp->ch_lock); - if (!glink_rx_intent_exists(devp->handle, count)) { + if (!glink_pkt_read_avail(devp) && + !glink_rx_intent_exists(devp->handle, count)) { ret = glink_queue_rx_intent(devp->handle, devp, count); if (ret) { - GLINK_PKT_ERR("%s: failed to queue_rx_intent ret[%d]\n", + GLINK_PKT_ERR("%s: failed to queue intent ret[%d]\n", __func__, ret); mutex_unlock(&devp->ch_lock); return ret; @@ -915,6 +936,7 @@ static long glink_pkt_ioctl(struct file *file, unsigned int cmd, case GLINK_PKT_IOCTL_QUEUE_RX_INTENT: ret = get_user(size, (uint32_t *)arg); GLINK_PKT_INFO("%s: intent size[%d]\n", __func__, size); + devp->auto_intent_enabled = false; ret = glink_queue_rx_intent(devp->handle, devp, size); if (ret) { GLINK_PKT_ERR("%s: failed to QUEUE_RX_INTENT ret[%d]\n", @@ -1183,6 +1205,7 @@ static int glink_pkt_init_add_device(struct glink_pkt_dev *devp, int i) glink_pkt_link_state_cb; devp->i = i; devp->poll_mode = 0; + devp->auto_intent_enabled = true; devp->ws_locked = 0; devp->ch_state = GLINK_LOCAL_DISCONNECTED; /* Default timeout for open wait is 120sec */ diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c index ed8006cacc08..ace2bc4f30b6 100644 --- a/drivers/soc/qcom/peripheral-loader.c +++ b/drivers/soc/qcom/peripheral-loader.c @@ -39,6 +39,8 @@ #include <asm/uaccess.h> #include <asm/setup.h> #include <asm-generic/io-64-nonatomic-lo-hi.h> +#define CREATE_TRACE_POINTS +#include <trace/events/trace_msm_pil_event.h> #include "peripheral-loader.h" @@ -835,6 +837,7 @@ int pil_boot(struct pil_desc *desc) goto release_fw; } + trace_pil_event("before_init_image", desc); if (desc->ops->init_image) ret = desc->ops->init_image(desc, fw->data, fw->size); if (ret) { @@ -843,6 +846,7 @@ int pil_boot(struct pil_desc *desc) goto err_boot; } + trace_pil_event("before_mem_setup", desc); if (desc->ops->mem_setup) ret = desc->ops->mem_setup(desc, priv->region_start, priv->region_end - priv->region_start); @@ -858,6 +862,7 @@ int pil_boot(struct pil_desc *desc) * Also for secure boot devices, modem memory has to be released * after MBA is booted */ + trace_pil_event("before_assign_mem", desc); if (desc->modem_ssr) { ret = pil_assign_mem_to_linux(desc, priv->region_start, (priv->region_end - priv->region_start)); @@ -876,6 +881,7 @@ int pil_boot(struct pil_desc *desc) hyp_assign = true; } + trace_pil_event("before_load_seg", desc); list_for_each_entry(seg, &desc->priv->segs, list) { ret = pil_load_seg(desc, seg); if (ret) @@ -883,6 +889,7 @@ int pil_boot(struct pil_desc *desc) } if (desc->subsys_vmid > 0) { + trace_pil_event("before_reclaim_mem", desc); ret = pil_reclaim_mem(desc, priv->region_start, (priv->region_end - priv->region_start), desc->subsys_vmid); @@ -894,12 +901,14 @@ int pil_boot(struct pil_desc *desc) hyp_assign = false; } + trace_pil_event("before_auth_reset", desc); ret = desc->ops->auth_and_reset(desc); if (ret) { pil_err(desc, "Failed to bring out of reset(rc:%d)\n", ret); subsys_set_error(desc->subsys_dev, firmware_error_msg); goto err_auth_and_reset; } + trace_pil_event("reset_done", desc); pil_info(desc, "Brought out of reset\n"); desc->modem_ssr = false; err_auth_and_reset: diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c index 988b6e8c9fd9..5fcb0f95733c 100644 --- a/drivers/soc/qcom/pil-msa.c +++ b/drivers/soc/qcom/pil-msa.c @@ -26,6 +26,7 @@ #include <linux/highmem.h> #include <soc/qcom/scm.h> #include <soc/qcom/secure_buffer.h> +#include <trace/events/trace_msm_pil_event.h> #include "peripheral-loader.h" #include "pil-q6v5.h" @@ -462,6 +463,7 @@ static int pil_mss_reset(struct pil_desc *pil) phys_addr_t start_addr = pil_get_entry_addr(pil); int ret; + trace_pil_func(__func__); if (drv->mba_dp_phys) start_addr = drv->mba_dp_phys; @@ -555,6 +557,7 @@ int pil_mss_reset_load_mba(struct pil_desc *pil) const u8 *data; struct device *dma_dev = md->mba_mem_dev_fixed ?: &md->mba_mem_dev; + trace_pil_func(__func__); fw_name_p = drv->non_elf_image ? fw_name_legacy : fw_name; ret = request_firmware(&fw, fw_name_p, pil->dev); if (ret) { @@ -685,6 +688,7 @@ static int pil_msa_auth_modem_mdt(struct pil_desc *pil, const u8 *metadata, DEFINE_DMA_ATTRS(attrs); + trace_pil_func(__func__); dma_dev->coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8); dma_set_attr(DMA_ATTR_SKIP_ZEROING, &attrs); dma_set_attr(DMA_ATTR_STRONGLY_ORDERED, &attrs); diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c index 34228f072b28..15415e78f98e 100644 --- a/drivers/soc/qcom/pil-q6v5.c +++ b/drivers/soc/qcom/pil-q6v5.c @@ -22,6 +22,7 @@ #include <linux/regulator/consumer.h> #include <linux/regulator/rpm-smd-regulator.h> #include <linux/clk/msm-clk.h> +#include <trace/events/trace_msm_pil_event.h> #include "peripheral-loader.h" #include "pil-q6v5.h" @@ -360,6 +361,7 @@ static int __pil_q6v55_reset(struct pil_desc *pil) u32 val; int i; + trace_pil_func(__func__); /* Override the ACC value if required */ if (drv->override_acc) writel_relaxed(QDSP6SS_ACC_OVERRIDE_VAL, diff --git a/drivers/soc/qcom/rpm-smd-debug.c b/drivers/soc/qcom/rpm-smd-debug.c index 4e406f7cd379..6ef90b23aed5 100644 --- a/drivers/soc/qcom/rpm-smd-debug.c +++ b/drivers/soc/qcom/rpm-smd-debug.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -44,9 +44,9 @@ static ssize_t rsc_ops_write(struct file *fp, const char __user *user_buffer, { char buf[MAX_MSG_BUFFER], rsc_type_str[6] = {}, rpm_set[8] = {}, key_str[6] = {}; - int i, pos, set = -1, nelems; + int i, pos = -1, set = -1, nelems = -1; char *cmp; - uint32_t rsc_type, rsc_id, key, data; + uint32_t rsc_type = 0, rsc_id = 0, key = 0, data = 0; struct msm_rpm_request *req; count = min(count, sizeof(buf) - 1); @@ -55,8 +55,12 @@ static ssize_t rsc_ops_write(struct file *fp, const char __user *user_buffer, buf[count] = '\0'; cmp = strstrip(buf); - sscanf(cmp, "%7s %5s %u %d %n", rpm_set, rsc_type_str, &rsc_id, - &nelems, &pos); + if (sscanf(cmp, "%7s %5s %u %d %n", rpm_set, rsc_type_str, + &rsc_id, &nelems, &pos) != 4) { + pr_err("Invalid number of arguments passed\n"); + goto err; + } + if (strlen(rpm_set) > 6 || strlen(rsc_type_str) > 4) { pr_err("Invalid value of set or resource type\n"); goto err; @@ -84,7 +88,11 @@ static ssize_t rsc_ops_write(struct file *fp, const char __user *user_buffer, for (i = 0; i < nelems; i++) { cmp += pos; - sscanf(cmp, "%5s %n", key_str, &pos); + if (sscanf(cmp, "%5s %n", key_str, &pos) != 1) { + pr_err("Invalid number of arguments passed\n"); + goto err; + } + if (strlen(key_str) > 4) { pr_err("Key value cannot be more than 4 charecters"); goto err; @@ -96,7 +104,11 @@ static ssize_t rsc_ops_write(struct file *fp, const char __user *user_buffer, } cmp += pos; - sscanf(cmp, "%u %n", &data, &pos); + if (sscanf(cmp, "%u %n", &data, &pos) != 1) { + pr_err("Invalid number of arguments passed\n"); + goto err; + } + if (msm_rpm_add_kvp_data(req, key, (void *)&data, sizeof(data))) goto err_request; diff --git a/drivers/soc/qcom/rpm_master_stat.c b/drivers/soc/qcom/rpm_master_stat.c index 7bf18ffe6ad2..b8bf3a059677 100644 --- a/drivers/soc/qcom/rpm_master_stat.c +++ b/drivers/soc/qcom/rpm_master_stat.c @@ -50,6 +50,8 @@ #define GET_FIELD(a) ((strnstr(#a, ".", 80) + 1)) +static DEFINE_MUTEX(msm_rpm_master_stats_mutex); + struct msm_rpm_master_stats { uint32_t active_cores; uint32_t numshutdowns; @@ -80,9 +82,11 @@ int msm_rpm_master_stats_file_close(struct inode *inode, { struct msm_rpm_master_stats_private_data *private = file->private_data; + mutex_lock(&msm_rpm_master_stats_mutex); if (private->reg_base) iounmap(private->reg_base); kfree(file->private_data); + mutex_unlock(&msm_rpm_master_stats_mutex); return 0; } @@ -95,15 +99,11 @@ static int msm_rpm_master_copy_stats( static int master_cnt; int count, j = 0; char *buf; - static DEFINE_MUTEX(msm_rpm_master_stats_mutex); unsigned long active_cores; - mutex_lock(&msm_rpm_master_stats_mutex); - /* Iterate possible number of masters */ if (master_cnt > prvdata->num_masters - 1) { master_cnt = 0; - mutex_unlock(&msm_rpm_master_stats_mutex); return 0; } @@ -256,7 +256,6 @@ static int msm_rpm_master_copy_stats( } master_cnt++; - mutex_unlock(&msm_rpm_master_stats_mutex); return RPM_MASTERS_BUF_LEN - count; } @@ -265,25 +264,36 @@ static ssize_t msm_rpm_master_stats_file_read(struct file *file, { struct msm_rpm_master_stats_private_data *prvdata; struct msm_rpm_master_stats_platform_data *pdata; + ssize_t ret; + mutex_lock(&msm_rpm_master_stats_mutex); prvdata = file->private_data; - if (!prvdata) - return -EINVAL; + if (!prvdata) { + ret = -EINVAL; + goto exit; + } pdata = prvdata->platform_data; - if (!pdata) - return -EINVAL; + if (!pdata) { + ret = -EINVAL; + goto exit; + } - if (!bufu || count == 0) - return -EINVAL; + if (!bufu || count == 0) { + ret = -EINVAL; + goto exit; + } if (*ppos <= pdata->phys_size) { prvdata->len = msm_rpm_master_copy_stats(prvdata); *ppos = 0; } - return simple_read_from_buffer(bufu, count, ppos, + ret = simple_read_from_buffer(bufu, count, ppos, prvdata->buf, prvdata->len); +exit: + mutex_unlock(&msm_rpm_master_stats_mutex); + return ret; } static int msm_rpm_master_stats_file_open(struct inode *inode, @@ -291,15 +301,20 @@ static int msm_rpm_master_stats_file_open(struct inode *inode, { struct msm_rpm_master_stats_private_data *prvdata; struct msm_rpm_master_stats_platform_data *pdata; + int ret = 0; + mutex_lock(&msm_rpm_master_stats_mutex); pdata = inode->i_private; file->private_data = kzalloc(sizeof(struct msm_rpm_master_stats_private_data), GFP_KERNEL); - if (!file->private_data) - return -ENOMEM; + if (!file->private_data) { + ret = -ENOMEM; + goto exit; + } + prvdata = file->private_data; prvdata->reg_base = ioremap(pdata->phys_addr_base, @@ -310,14 +325,17 @@ static int msm_rpm_master_stats_file_open(struct inode *inode, pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n", __func__, &pdata->phys_addr_base, pdata->phys_size); - return -EBUSY; + ret = -EBUSY; + goto exit; } prvdata->len = 0; prvdata->num_masters = pdata->num_masters; prvdata->master_names = pdata->masters; prvdata->platform_data = pdata; - return 0; +exit: + mutex_unlock(&msm_rpm_master_stats_mutex); + return ret; } static const struct file_operations msm_rpm_master_stats_fops = { diff --git a/drivers/soc/qcom/rpm_rail_stats.c b/drivers/soc/qcom/rpm_rail_stats.c index 9ef96dc54eb6..728fb69bfbe2 100644 --- a/drivers/soc/qcom/rpm_rail_stats.c +++ b/drivers/soc/qcom/rpm_rail_stats.c @@ -46,6 +46,8 @@ #define NAMELEN (sizeof(uint32_t)+1) +static DEFINE_MUTEX(msm_rpm_rail_stats_mutex); + struct msm_rpm_rail_stats_platform_data { phys_addr_t phys_addr_base; u32 phys_size; @@ -80,9 +82,11 @@ int msm_rpm_rail_stats_file_close(struct inode *inode, struct file *file) { struct msm_rpm_rail_stats_private_data *private = file->private_data; + mutex_lock(&msm_rpm_rail_stats_mutex); if (private->reg_base) iounmap(private->reg_base); kfree(file->private_data); + mutex_unlock(&msm_rpm_rail_stats_mutex); return 0; } @@ -154,18 +158,26 @@ static int msm_rpm_rail_stats_copy( static ssize_t msm_rpm_rail_stats_file_read(struct file *file, char __user *bufu, size_t count, loff_t *ppos) { - struct msm_rpm_rail_stats_private_data *prvdata = - file->private_data; + struct msm_rpm_rail_stats_private_data *prvdata; struct msm_rpm_rail_stats_platform_data *pdata; + ssize_t ret; - if (!prvdata) - return -EINVAL; + mutex_lock(&msm_rpm_rail_stats_mutex); + prvdata = file->private_data; + if (!prvdata) { + ret = -EINVAL; + goto exit; + } - if (!prvdata->platform_data) - return -EINVAL; + if (!prvdata->platform_data) { + ret = -EINVAL; + goto exit; + } - if (!bufu || count == 0) - return -EINVAL; + if (!bufu || count == 0) { + ret = -EINVAL; + goto exit; + } pdata = prvdata->platform_data; @@ -174,22 +186,32 @@ static ssize_t msm_rpm_rail_stats_file_read(struct file *file, *ppos = 0; } - return simple_read_from_buffer(bufu, count, ppos, + ret = simple_read_from_buffer(bufu, count, ppos, prvdata->buf, prvdata->len); +exit: + mutex_unlock(&msm_rpm_rail_stats_mutex); + return ret; } static int msm_rpm_rail_stats_file_open(struct inode *inode, struct file *file) { struct msm_rpm_rail_stats_private_data *prvdata; - struct msm_rpm_rail_stats_platform_data *pdata = inode->i_private; + struct msm_rpm_rail_stats_platform_data *pdata; + int ret = 0; + + mutex_lock(&msm_rpm_rail_stats_mutex); + pdata = inode->i_private; file->private_data = kzalloc(sizeof(struct msm_rpm_rail_stats_private_data), GFP_KERNEL); - if (!file->private_data) - return -ENOMEM; + if (!file->private_data) { + ret = -ENOMEM; + goto exit; + } + prvdata = file->private_data; prvdata->reg_base = ioremap(pdata->phys_addr_base, @@ -200,12 +222,15 @@ static int msm_rpm_rail_stats_file_open(struct inode *inode, pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n", __func__, &pdata->phys_addr_base, pdata->phys_size); - return -EBUSY; + ret = -EBUSY; + goto exit; } prvdata->len = 0; prvdata->platform_data = pdata; - return 0; +exit: + mutex_unlock(&msm_rpm_rail_stats_mutex); + return ret; } diff --git a/drivers/soc/qcom/rpm_stats.c b/drivers/soc/qcom/rpm_stats.c index 8f3094853ba3..b54af9eae8ec 100644 --- a/drivers/soc/qcom/rpm_stats.c +++ b/drivers/soc/qcom/rpm_stats.c @@ -31,6 +31,8 @@ #define GET_PDATA_OF_ATTR(attr) \ (container_of(attr, struct msm_rpmstats_kobj_attr, ka)->pd) +static DEFINE_MUTEX(rpm_stats_mutex); + enum { ID_COUNTER, ID_ACCUM_TIME_SCLK, @@ -220,6 +222,12 @@ static int msm_rpmstats_copy_stats(struct msm_rpmstats_private_data *pdata) record.id = msm_rpmstats_read_register(pdata->reg_base, pdata->read_idx, 1); + if (record.id >= ID_MAX) { + pr_err("%s: array out of bound error found.\n", + __func__); + return -EINVAL; + } + record.val = msm_rpmstats_read_register(pdata->reg_base, pdata->read_idx, 2); @@ -242,13 +250,20 @@ static ssize_t msm_rpmstats_file_read(struct file *file, char __user *bufu, size_t count, loff_t *ppos) { struct msm_rpmstats_private_data *prvdata; + ssize_t ret; + mutex_lock(&rpm_stats_mutex); prvdata = file->private_data; - if (!prvdata) - return -EINVAL; - if (!bufu || count == 0) - return -EINVAL; + if (!prvdata) { + ret = -EINVAL; + goto exit; + } + + if (!bufu || count == 0) { + ret = -EINVAL; + goto exit; + } if (prvdata->platform_data->version == 1) { if (!prvdata->num_records) @@ -263,22 +278,30 @@ static ssize_t msm_rpmstats_file_read(struct file *file, char __user *bufu, prvdata->len = msm_rpmstats_copy_stats_v2(prvdata); *ppos = 0; } - return simple_read_from_buffer(bufu, count, ppos, + ret = simple_read_from_buffer(bufu, count, ppos, prvdata->buf, prvdata->len); +exit: + mutex_unlock(&rpm_stats_mutex); + return ret; } static int msm_rpmstats_file_open(struct inode *inode, struct file *file) { struct msm_rpmstats_private_data *prvdata; struct msm_rpmstats_platform_data *pdata; + int ret = 0; + mutex_lock(&rpm_stats_mutex); pdata = inode->i_private; file->private_data = kmalloc(sizeof(struct msm_rpmstats_private_data), GFP_KERNEL); - if (!file->private_data) - return -ENOMEM; + if (!file->private_data) { + ret = -ENOMEM; + goto exit; + } + prvdata = file->private_data; prvdata->reg_base = ioremap_nocache(pdata->phys_addr_base, @@ -289,24 +312,28 @@ static int msm_rpmstats_file_open(struct inode *inode, struct file *file) pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n", __func__, &pdata->phys_addr_base, pdata->phys_size); - return -EBUSY; + ret = -EBUSY; + goto exit; } prvdata->read_idx = prvdata->num_records = prvdata->len = 0; prvdata->platform_data = pdata; if (pdata->version == 2) prvdata->num_records = 2; - - return 0; +exit: + mutex_unlock(&rpm_stats_mutex); + return ret; } static int msm_rpmstats_file_close(struct inode *inode, struct file *file) { struct msm_rpmstats_private_data *private = file->private_data; + mutex_lock(&rpm_stats_mutex); if (private->reg_base) iounmap(private->reg_base); kfree(file->private_data); + mutex_unlock(&rpm_stats_mutex); return 0; } @@ -362,22 +389,26 @@ static ssize_t rpmstats_show(struct kobject *kobj, { struct msm_rpmstats_private_data *prvdata = NULL; struct msm_rpmstats_platform_data *pdata = NULL; + ssize_t ret; + mutex_lock(&rpm_stats_mutex); pdata = GET_PDATA_OF_ATTR(attr); prvdata = kmalloc(sizeof(*prvdata), GFP_KERNEL); - if (!prvdata) - return -ENOMEM; + if (!prvdata) { + ret = -ENOMEM; + goto kmalloc_fail; + } prvdata->reg_base = ioremap_nocache(pdata->phys_addr_base, pdata->phys_size); if (!prvdata->reg_base) { - kfree(prvdata); pr_err("%s: ERROR could not ioremap start=%pa, len=%u\n", __func__, &pdata->phys_addr_base, pdata->phys_size); - return -EBUSY; + ret = -EBUSY; + goto ioremap_fail; } prvdata->read_idx = prvdata->num_records = prvdata->len = 0; @@ -399,23 +430,22 @@ static ssize_t rpmstats_show(struct kobject *kobj, prvdata); } - return snprintf(buf, prvdata->len, prvdata->buf); + ret = snprintf(buf, prvdata->len, prvdata->buf); + iounmap(prvdata->reg_base); +ioremap_fail: + kfree(prvdata); +kmalloc_fail: + mutex_unlock(&rpm_stats_mutex); + return ret; } static int msm_rpmstats_create_sysfs(struct msm_rpmstats_platform_data *pd) { - struct kobject *module_kobj = NULL; struct kobject *rpmstats_kobj = NULL; struct msm_rpmstats_kobj_attr *rpms_ka = NULL; int ret = 0; - module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME); - if (!module_kobj) { - pr_err("%s: Cannot find module_kset\n", __func__); - return -ENODEV; - } - - rpmstats_kobj = kobject_create_and_add("rpmstats", module_kobj); + rpmstats_kobj = kobject_create_and_add("system_sleep", power_kobj); if (!rpmstats_kobj) { pr_err("%s: Cannot create rpmstats kobject\n", __func__); ret = -ENOMEM; diff --git a/drivers/soc/qcom/scm.c b/drivers/soc/qcom/scm.c index 36804b988d62..31de6e5c173c 100644 --- a/drivers/soc/qcom/scm.c +++ b/drivers/soc/qcom/scm.c @@ -731,10 +731,6 @@ int scm_call2_atomic(u32 fn_id, struct scm_desc *desc) x0 = fn_id | BIT(SMC_ATOMIC_SYSCALL) | scm_version_mask; - pr_debug("scm_call: func id %#llx, args: %#x, %#llx, %#llx, %#llx, %#llx\n", - x0, desc->arginfo, desc->args[0], desc->args[1], - desc->args[2], desc->x5); - if (scm_version == SCM_ARMV8_64) ret = __scm_call_armv8_64(x0, desc->arginfo, desc->args[0], desc->args[1], desc->args[2], @@ -746,9 +742,8 @@ int scm_call2_atomic(u32 fn_id, struct scm_desc *desc) desc->x5, &desc->ret[0], &desc->ret[1], &desc->ret[2]); if (ret < 0) - pr_err("scm_call failed: func id %#llx, arginfo: %#x, args: %#llx, %#llx, %#llx, %#llx, ret: %d, syscall returns: %#llx, %#llx, %#llx\n", - x0, desc->arginfo, desc->args[0], desc->args[1], - desc->args[2], desc->x5, ret, desc->ret[0], + pr_err("scm_call failed: func id %#llx, ret: %d, syscall returns: %#llx, %#llx, %#llx\n", + x0, ret, desc->ret[0], desc->ret[1], desc->ret[2]); if (arglen > N_REGISTER_ARGS) diff --git a/drivers/soc/qcom/smp2p_sleepstate.c b/drivers/soc/qcom/smp2p_sleepstate.c index 34b46d25a823..2ef25e48ce50 100644 --- a/drivers/soc/qcom/smp2p_sleepstate.c +++ b/drivers/soc/qcom/smp2p_sleepstate.c @@ -14,6 +14,8 @@ #include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/suspend.h> +#include <linux/delay.h> +#include <linux/ipc_router.h> #include "smp2p_private.h" #define SET_DELAY (2 * HZ) @@ -35,10 +37,14 @@ static int sleepstate_pm_notifier(struct notifier_block *nb, switch (event) { case PM_SUSPEND_PREPARE: gpio_set_value(slst_gpio_base_id + PROC_AWAKE_ID, 0); + msleep(25); /* To be tuned based on SMP2P latencies */ + msm_ipc_router_set_ws_allowed(true); break; case PM_POST_SUSPEND: gpio_set_value(slst_gpio_base_id + PROC_AWAKE_ID, 1); + msleep(25); /* To be tuned based on SMP2P latencies */ + msm_ipc_router_set_ws_allowed(false); break; } return NOTIFY_DONE; diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c index d1f300e6afb7..7f03aabf415e 100644 --- a/drivers/soc/qcom/spcom.c +++ b/drivers/soc/qcom/spcom.c @@ -96,8 +96,6 @@ /* SPCOM driver name */ #define DEVICE_NAME "spcom" -#define SPCOM_MAX_CHANNELS 0x20 - /* maximum ION buffers should be >= SPCOM_MAX_CHANNELS */ #define SPCOM_MAX_ION_BUF_PER_CH (SPCOM_MAX_CHANNELS + 4) diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c index 991bce363740..f85c4ba06b47 100644 --- a/drivers/soc/qcom/subsys-pil-tz.c +++ b/drivers/soc/qcom/subsys-pil-tz.c @@ -1113,6 +1113,7 @@ static int pil_tz_driver_probe(struct platform_device *pdev) rc = PTR_ERR(d->subsys); goto err_subsys; } + d->desc.subsys_dev = d->subsys; return 0; err_subsys: diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c index 6afe2fb8cd75..ae249f382339 100644 --- a/drivers/soc/qcom/subsystem_restart.c +++ b/drivers/soc/qcom/subsystem_restart.c @@ -35,6 +35,7 @@ #include <soc/qcom/subsystem_restart.h> #include <soc/qcom/subsystem_notif.h> #include <soc/qcom/sysmon.h> +#include <trace/events/trace_msm_pil_event.h> #include <asm/current.h> @@ -539,8 +540,10 @@ static void notify_each_subsys_device(struct subsys_device **list, notif_data.no_auth = dev->desc->no_auth; notif_data.pdev = pdev; + trace_pil_notif("before_send_notif", notif, dev->desc->fw_name); subsys_notif_queue_notification(dev->notify, notif, ¬if_data); + trace_pil_notif("after_send_notif", notif, dev->desc->fw_name); } } diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c index 3c9d8efd3956..5fe3c572628b 100644 --- a/drivers/soc/qcom/wcd-dsp-glink.c +++ b/drivers/soc/qcom/wcd-dsp-glink.c @@ -58,6 +58,7 @@ struct wdsp_glink_rsp_que { struct wdsp_glink_tx_buf { struct work_struct tx_work; + struct work_struct free_tx_work; /* Glink channel information */ struct wdsp_glink_ch *ch; @@ -125,6 +126,46 @@ static int wdsp_glink_close_ch(struct wdsp_glink_ch *ch); static int wdsp_glink_open_ch(struct wdsp_glink_ch *ch); /* + * wdsp_glink_free_tx_buf_work - Work function to free tx pkt + * work: Work structure + */ +static void wdsp_glink_free_tx_buf_work(struct work_struct *work) +{ + struct wdsp_glink_tx_buf *tx_buf; + + tx_buf = container_of(work, struct wdsp_glink_tx_buf, + free_tx_work); + vfree(tx_buf); +} + +/* + * wdsp_glink_free_tx_buf - Function to free tx buffer + * priv: Pointer to the channel + * pkt_priv: Pointer to the tx buffer + */ +static void wdsp_glink_free_tx_buf(const void *priv, const void *pkt_priv) +{ + struct wdsp_glink_tx_buf *tx_buf = (struct wdsp_glink_tx_buf *)pkt_priv; + struct wdsp_glink_priv *wpriv; + struct wdsp_glink_ch *ch; + + if (!priv) { + pr_err("%s: Invalid priv\n", __func__); + return; + } + if (!tx_buf) { + pr_err("%s: Invalid tx_buf\n", __func__); + return; + } + + ch = (struct wdsp_glink_ch *)priv; + wpriv = ch->wpriv; + /* Work queue to free tx pkt */ + INIT_WORK(&tx_buf->free_tx_work, wdsp_glink_free_tx_buf_work); + queue_work(wpriv->work_queue, &tx_buf->free_tx_work); +} + +/* * wdsp_glink_notify_rx - Glink notify rx callback for responses * handle: Opaque Channel handle returned by GLink * priv: Private pointer to the channel @@ -183,14 +224,8 @@ static void wdsp_glink_notify_rx(void *handle, const void *priv, static void wdsp_glink_notify_tx_done(void *handle, const void *priv, const void *pkt_priv, const void *ptr) { - if (!pkt_priv) { - pr_err("%s: Invalid parameter\n", __func__); - return; - } - /* Free tx pkt */ - vfree(pkt_priv); + wdsp_glink_free_tx_buf(priv, pkt_priv); } - /* * wdsp_glink_notify_tx_abort - Glink notify tx abort callback to * free tx buffer @@ -201,12 +236,7 @@ static void wdsp_glink_notify_tx_done(void *handle, const void *priv, static void wdsp_glink_notify_tx_abort(void *handle, const void *priv, const void *pkt_priv) { - if (!pkt_priv) { - pr_err("%s: Invalid parameter\n", __func__); - return; - } - /* Free tx pkt */ - vfree(pkt_priv); + wdsp_glink_free_tx_buf(priv, pkt_priv); } /* @@ -555,7 +585,7 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, goto done; } ch = kcalloc(no_of_channels, sizeof(struct wdsp_glink_ch *), - GFP_KERNEL); + GFP_ATOMIC); if (!ch) { ret = -ENOMEM; goto done; diff --git a/drivers/soundwire/soundwire.c b/drivers/soundwire/soundwire.c index 6691418b516e..63545651fe43 100755 --- a/drivers/soundwire/soundwire.c +++ b/drivers/soundwire/soundwire.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -68,6 +68,27 @@ static void swr_dev_release(struct device *dev) } /** + * swr_remove_device - remove a soundwire device + * @swr_dev: soundwire device to remove + * + * Remove a soundwire device. Go through the soundwire + * device list that master has and remove swr_dev from + * it. + */ +void swr_remove_device(struct swr_device *swr_dev) +{ + struct swr_device *swr_dev_loop, *safe; + + list_for_each_entry_safe(swr_dev_loop, safe, + &swr_dev->master->devices, + dev_list) { + if (swr_dev == swr_dev_loop) + list_del(&swr_dev_loop->dev_list); + } +} +EXPORT_SYMBOL(swr_remove_device); + +/** * swr_new_device - instantiate a new soundwire device * @master: Controller to which device is connected * @info: Describes the soundwire device @@ -129,47 +150,6 @@ err_out: EXPORT_SYMBOL(swr_new_device); /** - * swr_startup_devices - perform additional initialization for child devices - * - * @swr_dev: pointer to soundwire slave device - * - * Performs any additional initialization needed for a soundwire slave device. - * This is a optional functionality defined by slave devices. - * Removes the slave node from the list, in case there is any failure. - */ -int swr_startup_devices(struct swr_device *swr_dev) -{ - struct swr_driver *swr_drv; - struct device *dev; - int ret = 0; - - if (!swr_dev) - return -EINVAL; - - dev = &swr_dev->dev; - if (!dev) - return -EINVAL; - - swr_drv = to_swr_driver(dev->driver); - if (!swr_drv) - return -EINVAL; - - if (swr_drv->startup) { - ret = swr_drv->startup(swr_dev); - if (ret) - goto out; - - dev_dbg(&swr_dev->dev, - "%s: startup complete for device %lx\n", - __func__, swr_dev->addr); - } - -out: - return ret; -} -EXPORT_SYMBOL(swr_startup_devices); - -/** * of_register_swr_devices - register child devices on to the soundwire bus * @master: pointer to soundwire master device * @@ -203,14 +183,15 @@ int of_register_swr_devices(struct swr_master *master) } info.addr = addr; info.of_node = of_node_get(node); + master->num_dev++; swr = swr_new_device(master, &info); if (!swr) { dev_err(&master->dev, "of_swr: Register failed %s\n", node->full_name); of_node_put(node); + master->num_dev--; continue; } - master->num_dev++; } return 0; } @@ -610,7 +591,7 @@ int swr_device_up(struct swr_device *swr_dev) dev = &swr_dev->dev; sdrv = to_swr_driver(dev->driver); if (!sdrv) - return -EINVAL; + return 0; if (sdrv->device_up) return sdrv->device_up(to_swr_device(dev)); @@ -638,7 +619,7 @@ int swr_device_down(struct swr_device *swr_dev) dev = &swr_dev->dev; sdrv = to_swr_driver(dev->driver); if (!sdrv) - return -EINVAL; + return 0; if (sdrv->device_down) return sdrv->device_down(to_swr_device(dev)); diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c index e72663bd2138..cdaf009c5b1f 100644 --- a/drivers/soundwire/swr-wcd-ctrl.c +++ b/drivers/soundwire/swr-wcd-ctrl.c @@ -540,7 +540,7 @@ static int swrm_read(struct swr_master *master, u8 dev_num, u16 reg_addr, { struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); int ret = 0; - int val; + int val = 0; u8 *reg_val = (u8 *)buf; if (!swrm) { @@ -1369,7 +1369,6 @@ static int swrm_probe(struct platform_device *pdev) { struct swr_mstr_ctrl *swrm; struct swr_ctrl_platform_data *pdata; - struct swr_device *swr_dev, *safe; int ret; /* Allocate soundwire master driver structure */ @@ -1470,9 +1469,6 @@ static int swrm_probe(struct platform_device *pdev) goto err_mstr_fail; } - if (pdev->dev.of_node) - of_register_swr_devices(&swrm->master); - /* Add devices registered with board-info as the controller will be up now */ @@ -1489,15 +1485,11 @@ static int swrm_probe(struct platform_device *pdev) } swrm->version = swrm->read(swrm->handle, SWRM_COMP_HW_VERSION); - /* Enumerate slave devices */ - list_for_each_entry_safe(swr_dev, safe, &swrm->master.devices, - dev_list) { - ret = swr_startup_devices(swr_dev); - if (ret) - list_del(&swr_dev->dev_list); - } mutex_unlock(&swrm->mlock); + if (pdev->dev.of_node) + of_register_swr_devices(&swrm->master); + dbgswrm = swrm; debugfs_swrm_dent = debugfs_create_dir(dev_name(&pdev->dev), 0); if (!IS_ERR(debugfs_swrm_dent)) { diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c index 548efd11e856..4499dd35f2dd 100644 --- a/drivers/spi/spi_qsd.c +++ b/drivers/spi/spi_qsd.c @@ -1494,11 +1494,11 @@ static inline void msm_spi_set_cs(struct spi_device *spi, bool set_flag) struct msm_spi *dd = spi_master_get_devdata(spi->master); u32 spi_ioc; u32 spi_ioc_orig; - int rc; + int rc = 0; rc = pm_runtime_get_sync(dd->dev); if (rc < 0) { - dev_err(dd->dev, "Failure during runtime get"); + dev_err(dd->dev, "Failure during runtime get,rc=%d", rc); return; } @@ -1513,6 +1513,15 @@ static inline void msm_spi_set_cs(struct spi_device *spi, bool set_flag) if (!(spi->mode & SPI_CS_HIGH)) set_flag = !set_flag; + /* Serve only under mutex lock as RT suspend may cause a race */ + mutex_lock(&dd->core_lock); + if (dd->suspended) { + dev_err(dd->dev, "%s: SPI operational state=%d Invalid\n", + __func__, dd->suspended); + mutex_unlock(&dd->core_lock); + return; + } + spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL); spi_ioc_orig = spi_ioc; if (set_flag) @@ -1524,6 +1533,8 @@ static inline void msm_spi_set_cs(struct spi_device *spi, bool set_flag) writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL); if (dd->pdata->is_shared) put_local_resources(dd); + mutex_unlock(&dd->core_lock); + pm_runtime_mark_last_busy(dd->dev); pm_runtime_put_autosuspend(dd->dev); } @@ -2616,6 +2627,7 @@ static int msm_spi_pm_suspend_runtime(struct device *device) wait_event_interruptible(dd->continue_suspend, !dd->transfer_pending); + mutex_lock(&dd->core_lock); if (dd->pdata && !dd->pdata->is_shared && dd->use_dma) { msm_spi_bam_pipe_disconnect(dd, &dd->bam.prod); msm_spi_bam_pipe_disconnect(dd, &dd->bam.cons); @@ -2625,6 +2637,7 @@ static int msm_spi_pm_suspend_runtime(struct device *device) if (dd->pdata) msm_spi_clk_path_vote(dd, 0); + mutex_unlock(&dd->core_lock); suspend_exit: return 0; diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c index 205af6627b80..8deee007218b 100644 --- a/drivers/staging/android/lowmemorykiller.c +++ b/drivers/staging/android/lowmemorykiller.c @@ -146,7 +146,7 @@ int adjust_minadj(short *min_score_adj) static int lmk_vmpressure_notifier(struct notifier_block *nb, unsigned long action, void *data) { - int other_free, other_file; + int other_free = 0, other_file = 0; unsigned long pressure = action; int array_size = ARRAY_SIZE(lowmem_adj); @@ -429,6 +429,7 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc) global_page_state(NR_FILE_PAGES) + zcache_pages()) other_file = global_page_state(NR_FILE_PAGES) + zcache_pages() - global_page_state(NR_SHMEM) - + global_page_state(NR_UNEVICTABLE) - total_swapcache_pages(); else other_file = 0; diff --git a/drivers/staging/comedi/drivers/jr3_pci.c b/drivers/staging/comedi/drivers/jr3_pci.c index b87192e0f9aa..109becdabc24 100644 --- a/drivers/staging/comedi/drivers/jr3_pci.c +++ b/drivers/staging/comedi/drivers/jr3_pci.c @@ -610,7 +610,7 @@ static void jr3_pci_poll_dev(unsigned long data) s = &dev->subdevices[i]; spriv = s->private; - if (now > spriv->next_time_min) { + if (time_after_eq(now, spriv->next_time_min)) { struct jr3_pci_poll_delay sub_delay; sub_delay = jr3_pci_poll_subdevice(s); @@ -726,11 +726,12 @@ static int jr3_pci_auto_attach(struct comedi_device *dev, s->insn_read = jr3_pci_ai_insn_read; spriv = jr3_pci_alloc_spriv(dev, s); - if (spriv) { - /* Channel specific range and maxdata */ - s->range_table_list = spriv->range_table_list; - s->maxdata_list = spriv->maxdata_list; - } + if (!spriv) + return -ENOMEM; + + /* Channel specific range and maxdata */ + s->range_table_list = spriv->range_table_list; + s->maxdata_list = spriv->maxdata_list; } /* Reset DSP card */ diff --git a/drivers/staging/gdm724x/gdm_mux.c b/drivers/staging/gdm724x/gdm_mux.c index 445f83615575..fb4f3fea6c66 100644 --- a/drivers/staging/gdm724x/gdm_mux.c +++ b/drivers/staging/gdm724x/gdm_mux.c @@ -670,14 +670,14 @@ static int __init gdm_usb_mux_init(void) static void __exit gdm_usb_mux_exit(void) { - unregister_lte_tty_driver(); - if (mux_rx_wq) { flush_workqueue(mux_rx_wq); destroy_workqueue(mux_rx_wq); } usb_deregister(&gdm_mux_driver); + unregister_lte_tty_driver(); + } module_init(gdm_usb_mux_init); diff --git a/drivers/staging/lustre/lustre/lov/lov_pack.c b/drivers/staging/lustre/lustre/lov/lov_pack.c index 2fb1e974cc70..e11b1001d1f6 100644 --- a/drivers/staging/lustre/lustre/lov/lov_pack.c +++ b/drivers/staging/lustre/lustre/lov/lov_pack.c @@ -399,18 +399,10 @@ int lov_getstripe(struct obd_export *exp, struct lov_stripe_md *lsm, struct lov_mds_md *lmmk = NULL; int rc, lmm_size; int lum_size; - mm_segment_t seg; if (!lsm) return -ENODATA; - /* - * "Switch to kernel segment" to allow copying from kernel space by - * copy_{to,from}_user(). - */ - seg = get_fs(); - set_fs(KERNEL_DS); - /* we only need the header part from user space to get lmm_magic and * lmm_stripe_count, (the header part is common to v1 and v3) */ lum_size = sizeof(struct lov_user_md_v1); @@ -485,6 +477,5 @@ int lov_getstripe(struct obd_export *exp, struct lov_stripe_md *lsm, obd_free_diskmd(exp, &lmmk); out_set: - set_fs(seg); return rc; } diff --git a/drivers/staging/rtl8188eu/core/rtw_ap.c b/drivers/staging/rtl8188eu/core/rtw_ap.c index 3cdb40fea5ee..f5cedbbc552a 100644 --- a/drivers/staging/rtl8188eu/core/rtw_ap.c +++ b/drivers/staging/rtl8188eu/core/rtw_ap.c @@ -894,7 +894,7 @@ int rtw_check_beacon_data(struct adapter *padapter, u8 *pbuf, int len) return _FAIL; - if (len > MAX_IE_SZ) + if (len < 0 || len > MAX_IE_SZ) return _FAIL; pbss_network->IELength = len; diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c index e9c4f973bba9..7a8ceb961bb6 100644 --- a/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c +++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_dev.c @@ -97,8 +97,9 @@ void rtl92e_set_reg(struct net_device *dev, u8 variable, u8 *val) switch (variable) { case HW_VAR_BSSID: - rtl92e_writel(dev, BSSIDR, ((u32 *)(val))[0]); - rtl92e_writew(dev, BSSIDR+2, ((u16 *)(val+2))[0]); + /* BSSIDR 2 byte alignment */ + rtl92e_writew(dev, BSSIDR, *(u16 *)val); + rtl92e_writel(dev, BSSIDR + 2, *(u32 *)(val + 2)); break; case HW_VAR_MEDIA_STATUS: @@ -626,7 +627,7 @@ void rtl92e_get_eeprom_size(struct net_device *dev) struct r8192_priv *priv = rtllib_priv(dev); RT_TRACE(COMP_INIT, "===========>%s()\n", __func__); - curCR = rtl92e_readl(dev, EPROM_CMD); + curCR = rtl92e_readw(dev, EPROM_CMD); RT_TRACE(COMP_INIT, "read from Reg Cmd9346CR(%x):%x\n", EPROM_CMD, curCR); priv->epromtype = (curCR & EPROM_CMD_9356SEL) ? EEPROM_93C56 : @@ -963,8 +964,8 @@ static void _rtl92e_net_update(struct net_device *dev) rtl92e_config_rate(dev, &rate_config); priv->dot11CurrentPreambleMode = PREAMBLE_AUTO; priv->basic_rate = rate_config &= 0x15f; - rtl92e_writel(dev, BSSIDR, ((u32 *)net->bssid)[0]); - rtl92e_writew(dev, BSSIDR+4, ((u16 *)net->bssid)[2]); + rtl92e_writew(dev, BSSIDR, *(u16 *)net->bssid); + rtl92e_writel(dev, BSSIDR + 2, *(u32 *)(net->bssid + 2)); if (priv->rtllib->iw_mode == IW_MODE_ADHOC) { rtl92e_writew(dev, ATIMWND, 2); @@ -1184,8 +1185,7 @@ void rtl92e_fill_tx_desc(struct net_device *dev, struct tx_desc *pdesc, struct cb_desc *cb_desc, struct sk_buff *skb) { struct r8192_priv *priv = rtllib_priv(dev); - dma_addr_t mapping = pci_map_single(priv->pdev, skb->data, skb->len, - PCI_DMA_TODEVICE); + dma_addr_t mapping; struct tx_fwinfo_8190pci *pTxFwInfo = NULL; pTxFwInfo = (struct tx_fwinfo_8190pci *)skb->data; @@ -1196,8 +1196,6 @@ void rtl92e_fill_tx_desc(struct net_device *dev, struct tx_desc *pdesc, pTxFwInfo->Short = _rtl92e_query_is_short(pTxFwInfo->TxHT, pTxFwInfo->TxRate, cb_desc); - if (pci_dma_mapping_error(priv->pdev, mapping)) - netdev_err(dev, "%s(): DMA Mapping error\n", __func__); if (cb_desc->bAMPDUEnable) { pTxFwInfo->AllowAggregation = 1; pTxFwInfo->RxMF = cb_desc->ampdu_factor; @@ -1232,6 +1230,14 @@ void rtl92e_fill_tx_desc(struct net_device *dev, struct tx_desc *pdesc, } memset((u8 *)pdesc, 0, 12); + + mapping = pci_map_single(priv->pdev, skb->data, skb->len, + PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(priv->pdev, mapping)) { + netdev_err(dev, "%s(): DMA Mapping error\n", __func__); + return; + } + pdesc->LINIP = 0; pdesc->CmdInit = 1; pdesc->Offset = sizeof(struct tx_fwinfo_8190pci) + 8; diff --git a/drivers/staging/vt6656/usbpipe.c b/drivers/staging/vt6656/usbpipe.c index c975c3b87093..cfc3017fd64a 100644 --- a/drivers/staging/vt6656/usbpipe.c +++ b/drivers/staging/vt6656/usbpipe.c @@ -50,15 +50,25 @@ int vnt_control_out(struct vnt_private *priv, u8 request, u16 value, u16 index, u16 length, u8 *buffer) { int status = 0; + u8 *usb_buffer; if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) return STATUS_FAILURE; mutex_lock(&priv->usb_lock); + usb_buffer = kmemdup(buffer, length, GFP_KERNEL); + if (!usb_buffer) { + mutex_unlock(&priv->usb_lock); + return -ENOMEM; + } + status = usb_control_msg(priv->usb, - usb_sndctrlpipe(priv->usb, 0), request, 0x40, value, - index, buffer, length, USB_CTL_WAIT); + usb_sndctrlpipe(priv->usb, 0), + request, 0x40, value, + index, usb_buffer, length, USB_CTL_WAIT); + + kfree(usb_buffer); mutex_unlock(&priv->usb_lock); @@ -78,15 +88,28 @@ int vnt_control_in(struct vnt_private *priv, u8 request, u16 value, u16 index, u16 length, u8 *buffer) { int status; + u8 *usb_buffer; if (test_bit(DEVICE_FLAGS_DISCONNECTED, &priv->flags)) return STATUS_FAILURE; mutex_lock(&priv->usb_lock); + usb_buffer = kmalloc(length, GFP_KERNEL); + if (!usb_buffer) { + mutex_unlock(&priv->usb_lock); + return -ENOMEM; + } + status = usb_control_msg(priv->usb, - usb_rcvctrlpipe(priv->usb, 0), request, 0xc0, value, - index, buffer, length, USB_CTL_WAIT); + usb_rcvctrlpipe(priv->usb, 0), + request, 0xc0, value, + index, usb_buffer, length, USB_CTL_WAIT); + + if (status == length) + memcpy(buffer, usb_buffer, length); + + kfree(usb_buffer); mutex_unlock(&priv->usb_lock); diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 6ed80b05d674..200d3de8bc1e 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -4821,6 +4821,7 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force) continue; } atomic_set(&sess->session_reinstatement, 1); + atomic_set(&sess->session_fall_back_to_erl0, 1); spin_unlock(&sess->conn_lock); list_move_tail(&se_sess->sess_list, &free_list); diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index b4bfd706ac94..dc1bd1f1bdfe 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -725,11 +725,8 @@ static ssize_t lio_target_nacl_cmdsn_depth_store(struct config_item *item, if (iscsit_get_tpg(tpg) < 0) return -EINVAL; - /* - * iscsit_tpg_set_initiator_node_queue_depth() assumes force=1 - */ - ret = iscsit_tpg_set_initiator_node_queue_depth(tpg, - config_item_name(acl_ci), cmdsn_depth, 1); + + ret = core_tpg_set_initiator_node_queue_depth(se_nacl, cmdsn_depth); pr_debug("LIO_Target_ConfigFS: %s/%s Set CmdSN Window: %u for" "InitiatorName: %s\n", config_item_name(wwn_ci), @@ -1593,42 +1590,31 @@ static int lio_tpg_check_prot_fabric_only( } /* - * Called with spin_lock_irq(struct se_portal_group->session_lock) held - * or not held. - * - * Also, this function calls iscsit_inc_session_usage_count() on the + * This function calls iscsit_inc_session_usage_count() on the * struct iscsi_session in question. */ static int lio_tpg_shutdown_session(struct se_session *se_sess) { struct iscsi_session *sess = se_sess->fabric_sess_ptr; - struct se_portal_group *se_tpg = se_sess->se_tpg; - bool local_lock = false; - - if (!spin_is_locked(&se_tpg->session_lock)) { - spin_lock_irq(&se_tpg->session_lock); - local_lock = true; - } + struct se_portal_group *se_tpg = &sess->tpg->tpg_se_tpg; + spin_lock_bh(&se_tpg->session_lock); spin_lock(&sess->conn_lock); if (atomic_read(&sess->session_fall_back_to_erl0) || atomic_read(&sess->session_logout) || (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) { spin_unlock(&sess->conn_lock); - if (local_lock) - spin_unlock_irq(&sess->conn_lock); + spin_unlock_bh(&se_tpg->session_lock); return 0; } atomic_set(&sess->session_reinstatement, 1); + atomic_set(&sess->session_fall_back_to_erl0, 1); spin_unlock(&sess->conn_lock); iscsit_stop_time2retain_timer(sess); - spin_unlock_irq(&se_tpg->session_lock); + spin_unlock_bh(&se_tpg->session_lock); iscsit_stop_session(sess, 1, 1); - if (!local_lock) - spin_lock_irq(&se_tpg->session_lock); - return 1; } diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 316f66172335..4a137b0ae3dc 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -195,6 +195,7 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn) initiatorname_param->value) && (sess_p->sess_ops->SessionType == sessiontype))) { atomic_set(&sess_p->session_reinstatement, 1); + atomic_set(&sess_p->session_fall_back_to_erl0, 1); spin_unlock(&sess_p->conn_lock); iscsit_inc_session_usage_count(sess_p); iscsit_stop_time2retain_timer(sess_p); diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index 68261b7dcefe..205a509b0dfb 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -589,16 +589,6 @@ int iscsit_tpg_del_network_portal( return iscsit_tpg_release_np(tpg_np, tpg, np); } -int iscsit_tpg_set_initiator_node_queue_depth( - struct iscsi_portal_group *tpg, - unsigned char *initiatorname, - u32 queue_depth, - int force) -{ - return core_tpg_set_initiator_node_queue_depth(&tpg->tpg_se_tpg, - initiatorname, queue_depth, force); -} - int iscsit_ta_authentication(struct iscsi_portal_group *tpg, u32 authentication) { unsigned char buf1[256], buf2[256], *none = NULL; diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h index 9db32bd24cd4..2da211920c18 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.h +++ b/drivers/target/iscsi/iscsi_target_tpg.h @@ -26,8 +26,6 @@ extern struct iscsi_tpg_np *iscsit_tpg_add_network_portal(struct iscsi_portal_gr int); extern int iscsit_tpg_del_network_portal(struct iscsi_portal_group *, struct iscsi_tpg_np *); -extern int iscsit_tpg_set_initiator_node_queue_depth(struct iscsi_portal_group *, - unsigned char *, u32, int); extern int iscsit_ta_authentication(struct iscsi_portal_group *, u32); extern int iscsit_ta_login_timeout(struct iscsi_portal_group *, u32); extern int iscsit_ta_netif_timeout(struct iscsi_portal_group *, u32); diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 79291869bce6..041a56987845 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -594,8 +594,7 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, if (ret < 0) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - if (ret) - target_complete_cmd(cmd, SAM_STAT_GOOD); + target_complete_cmd(cmd, SAM_STAT_GOOD); return 0; } diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 90c5dffc9fa4..608117819366 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -498,8 +498,11 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd, bool succes * been failed with a non-zero SCSI status. */ if (cmd->scsi_status) { - pr_err("compare_and_write_callback: non zero scsi_status:" + pr_debug("compare_and_write_callback: non zero scsi_status:" " 0x%02x\n", cmd->scsi_status); + *post_ret = 1; + if (cmd->scsi_status == SAM_STAT_CHECK_CONDITION) + ret = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; goto out; } diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 2794c6ec5c3c..899c33b3c734 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -169,28 +169,25 @@ void core_tpg_add_node_to_devs( mutex_unlock(&tpg->tpg_lun_mutex); } -/* core_set_queue_depth_for_node(): - * - * - */ -static int core_set_queue_depth_for_node( - struct se_portal_group *tpg, - struct se_node_acl *acl) +static void +target_set_nacl_queue_depth(struct se_portal_group *tpg, + struct se_node_acl *acl, u32 queue_depth) { + acl->queue_depth = queue_depth; + if (!acl->queue_depth) { - pr_err("Queue depth for %s Initiator Node: %s is 0," + pr_warn("Queue depth for %s Initiator Node: %s is 0," "defaulting to 1.\n", tpg->se_tpg_tfo->get_fabric_name(), acl->initiatorname); acl->queue_depth = 1; } - - return 0; } static struct se_node_acl *target_alloc_node_acl(struct se_portal_group *tpg, const unsigned char *initiatorname) { struct se_node_acl *acl; + u32 queue_depth; acl = kzalloc(max(sizeof(*acl), tpg->se_tpg_tfo->node_acl_size), GFP_KERNEL); @@ -205,24 +202,20 @@ static struct se_node_acl *target_alloc_node_acl(struct se_portal_group *tpg, spin_lock_init(&acl->nacl_sess_lock); mutex_init(&acl->lun_entry_mutex); atomic_set(&acl->acl_pr_ref_count, 0); + if (tpg->se_tpg_tfo->tpg_get_default_depth) - acl->queue_depth = tpg->se_tpg_tfo->tpg_get_default_depth(tpg); + queue_depth = tpg->se_tpg_tfo->tpg_get_default_depth(tpg); else - acl->queue_depth = 1; + queue_depth = 1; + target_set_nacl_queue_depth(tpg, acl, queue_depth); + snprintf(acl->initiatorname, TRANSPORT_IQN_LEN, "%s", initiatorname); acl->se_tpg = tpg; acl->acl_index = scsi_get_new_index(SCSI_AUTH_INTR_INDEX); tpg->se_tpg_tfo->set_default_node_attributes(acl); - if (core_set_queue_depth_for_node(tpg, acl) < 0) - goto out_free_acl; - return acl; - -out_free_acl: - kfree(acl); - return NULL; } static void target_add_node_acl(struct se_node_acl *acl) @@ -369,7 +362,8 @@ void core_tpg_del_initiator_node_acl(struct se_node_acl *acl) if (sess->sess_tearing_down != 0) continue; - target_get_session(sess); + if (!target_get_session(sess)) + continue; list_move(&sess->sess_acl_list, &sess_list); } spin_unlock_irqrestore(&acl->nacl_sess_lock, flags); @@ -406,108 +400,52 @@ void core_tpg_del_initiator_node_acl(struct se_node_acl *acl) * */ int core_tpg_set_initiator_node_queue_depth( - struct se_portal_group *tpg, - unsigned char *initiatorname, - u32 queue_depth, - int force) + struct se_node_acl *acl, + u32 queue_depth) { - struct se_session *sess, *init_sess = NULL; - struct se_node_acl *acl; + LIST_HEAD(sess_list); + struct se_portal_group *tpg = acl->se_tpg; + struct se_session *sess, *sess_tmp; unsigned long flags; - int dynamic_acl = 0; - - mutex_lock(&tpg->acl_node_mutex); - acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname); - if (!acl) { - pr_err("Access Control List entry for %s Initiator" - " Node %s does not exists for TPG %hu, ignoring" - " request.\n", tpg->se_tpg_tfo->get_fabric_name(), - initiatorname, tpg->se_tpg_tfo->tpg_get_tag(tpg)); - mutex_unlock(&tpg->acl_node_mutex); - return -ENODEV; - } - if (acl->dynamic_node_acl) { - acl->dynamic_node_acl = 0; - dynamic_acl = 1; - } - mutex_unlock(&tpg->acl_node_mutex); - - spin_lock_irqsave(&tpg->session_lock, flags); - list_for_each_entry(sess, &tpg->tpg_sess_list, sess_list) { - if (sess->se_node_acl != acl) - continue; - - if (!force) { - pr_err("Unable to change queue depth for %s" - " Initiator Node: %s while session is" - " operational. To forcefully change the queue" - " depth and force session reinstatement" - " use the \"force=1\" parameter.\n", - tpg->se_tpg_tfo->get_fabric_name(), initiatorname); - spin_unlock_irqrestore(&tpg->session_lock, flags); - - mutex_lock(&tpg->acl_node_mutex); - if (dynamic_acl) - acl->dynamic_node_acl = 1; - mutex_unlock(&tpg->acl_node_mutex); - return -EEXIST; - } - /* - * Determine if the session needs to be closed by our context. - */ - if (!tpg->se_tpg_tfo->shutdown_session(sess)) - continue; - - init_sess = sess; - break; - } + int rc; /* * User has requested to change the queue depth for a Initiator Node. * Change the value in the Node's struct se_node_acl, and call - * core_set_queue_depth_for_node() to add the requested queue depth. - * - * Finally call tpg->se_tpg_tfo->close_session() to force session - * reinstatement to occur if there is an active session for the - * $FABRIC_MOD Initiator Node in question. + * target_set_nacl_queue_depth() to set the new queue depth. */ - acl->queue_depth = queue_depth; + target_set_nacl_queue_depth(tpg, acl, queue_depth); + + spin_lock_irqsave(&acl->nacl_sess_lock, flags); + list_for_each_entry_safe(sess, sess_tmp, &acl->acl_sess_list, + sess_acl_list) { + if (sess->sess_tearing_down != 0) + continue; + if (!target_get_session(sess)) + continue; + spin_unlock_irqrestore(&acl->nacl_sess_lock, flags); - if (core_set_queue_depth_for_node(tpg, acl) < 0) { - spin_unlock_irqrestore(&tpg->session_lock, flags); /* - * Force session reinstatement if - * core_set_queue_depth_for_node() failed, because we assume - * the $FABRIC_MOD has already the set session reinstatement - * bit from tpg->se_tpg_tfo->shutdown_session() called above. + * Finally call tpg->se_tpg_tfo->close_session() to force session + * reinstatement to occur if there is an active session for the + * $FABRIC_MOD Initiator Node in question. */ - if (init_sess) - tpg->se_tpg_tfo->close_session(init_sess); - - mutex_lock(&tpg->acl_node_mutex); - if (dynamic_acl) - acl->dynamic_node_acl = 1; - mutex_unlock(&tpg->acl_node_mutex); - return -EINVAL; + rc = tpg->se_tpg_tfo->shutdown_session(sess); + target_put_session(sess); + if (!rc) { + spin_lock_irqsave(&acl->nacl_sess_lock, flags); + continue; + } + target_put_session(sess); + spin_lock_irqsave(&acl->nacl_sess_lock, flags); } - spin_unlock_irqrestore(&tpg->session_lock, flags); - /* - * If the $FABRIC_MOD session for the Initiator Node ACL exists, - * forcefully shutdown the $FABRIC_MOD session/nexus. - */ - if (init_sess) - tpg->se_tpg_tfo->close_session(init_sess); + spin_unlock_irqrestore(&acl->nacl_sess_lock, flags); pr_debug("Successfully changed queue depth to: %d for Initiator" - " Node: %s on %s Target Portal Group: %u\n", queue_depth, - initiatorname, tpg->se_tpg_tfo->get_fabric_name(), + " Node: %s on %s Target Portal Group: %u\n", acl->queue_depth, + acl->initiatorname, tpg->se_tpg_tfo->get_fabric_name(), tpg->se_tpg_tfo->tpg_get_tag(tpg)); - mutex_lock(&tpg->acl_node_mutex); - if (dynamic_acl) - acl->dynamic_node_acl = 1; - mutex_unlock(&tpg->acl_node_mutex); - return 0; } EXPORT_SYMBOL(core_tpg_set_initiator_node_queue_depth); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index df2059984e14..60743bf27f37 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -383,9 +383,9 @@ static void target_release_session(struct kref *kref) se_tpg->se_tpg_tfo->close_session(se_sess); } -void target_get_session(struct se_session *se_sess) +int target_get_session(struct se_session *se_sess) { - kref_get(&se_sess->sess_kref); + return kref_get_unless_zero(&se_sess->sess_kref); } EXPORT_SYMBOL(target_get_session); @@ -1154,15 +1154,28 @@ target_cmd_size_check(struct se_cmd *cmd, unsigned int size) if (cmd->unknown_data_length) { cmd->data_length = size; } else if (size != cmd->data_length) { - pr_warn("TARGET_CORE[%s]: Expected Transfer Length:" + pr_warn_ratelimited("TARGET_CORE[%s]: Expected Transfer Length:" " %u does not match SCSI CDB Length: %u for SAM Opcode:" " 0x%02x\n", cmd->se_tfo->get_fabric_name(), cmd->data_length, size, cmd->t_task_cdb[0]); - if (cmd->data_direction == DMA_TO_DEVICE && - cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) { - pr_err("Rejecting underflow/overflow WRITE data\n"); - return TCM_INVALID_CDB_FIELD; + if (cmd->data_direction == DMA_TO_DEVICE) { + if (cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) { + pr_err_ratelimited("Rejecting underflow/overflow" + " for WRITE data CDB\n"); + return TCM_INVALID_CDB_FIELD; + } + /* + * Some fabric drivers like iscsi-target still expect to + * always reject overflow writes. Reject this case until + * full fabric driver level support for overflow writes + * is introduced tree-wide. + */ + if (size > cmd->data_length) { + pr_err_ratelimited("Rejecting overflow for" + " WRITE control CDB\n"); + return TCM_INVALID_CDB_FIELD; + } } /* * Reject READ_* or WRITE_* with overflow/underflow for diff --git a/drivers/thermal/msm-tsens.c b/drivers/thermal/msm-tsens.c index d59b9736c570..5f955af4671a 100644 --- a/drivers/thermal/msm-tsens.c +++ b/drivers/thermal/msm-tsens.c @@ -2119,7 +2119,7 @@ static int get_device_tree_data(struct platform_device *pdev, { struct device_node *of_node = pdev->dev.of_node; struct resource *res_mem = NULL; - u32 *tsens_slope_data, *sensor_id, *client_id; + u32 *tsens_slope_data = NULL, *sensor_id, *client_id; u32 *temp1_calib_offset_factor, *temp2_calib_offset_factor; u32 rc = 0, i, tsens_num_sensors = 0; u32 cycle_monitor = 0, wd_bark = 0; diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c index c8cbd078bb07..b1f4c3f27111 100644 --- a/drivers/thermal/msm_thermal.c +++ b/drivers/thermal/msm_thermal.c @@ -7409,11 +7409,11 @@ static int msm_thermal_dev_probe(struct platform_device *pdev) pr_err("thermal pre init failed. err:%d\n", ret); goto probe_exit; } + probe_sensor_info(node, &data, pdev); ret = probe_deferrable_properties(node, &data, pdev); if (ret) goto probe_exit; - probe_sensor_info(node, &data, pdev); probe_cc(node, &data, pdev); probe_freq_mitigation(node, &data, pdev); probe_cx_phase_ctrl(node, &data, pdev); diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c index c78406cb3325..2df1bf69e7c9 100644 --- a/drivers/thermal/qpnp-adc-tm.c +++ b/drivers/thermal/qpnp-adc-tm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -3292,40 +3292,55 @@ static int qpnp_adc_tm_remove(struct platform_device *pdev) static void qpnp_adc_tm_shutdown(struct platform_device *pdev) { struct qpnp_adc_tm_chip *chip = dev_get_drvdata(&pdev->dev); - int rc = 0; + int rc = 0, i = 0; u8 reg_val = 0, status1 = 0, en_ctl1 = 0; - /* Set measurement in single measurement mode */ - reg_val = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT; - rc = qpnp_adc_tm_mode_select(chip, reg_val); - if (rc < 0) - pr_err("adc-tm single mode select failed\n"); + if (!chip->adc_tm_hc) { + /* Set measurement in single measurement mode */ + reg_val = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT; + rc = qpnp_adc_tm_mode_select(chip, reg_val); + if (rc < 0) + pr_err("adc-tm single mode select failed\n"); + } /* Disable bank */ rc = qpnp_adc_tm_disable(chip); if (rc < 0) pr_err("adc-tm disable failed\n"); - /* Check if a conversion is in progress */ - rc = qpnp_adc_tm_req_sts_check(chip); - if (rc < 0) - pr_err("adc-tm req_sts check failed\n"); + if (chip->adc_tm_hc) { + for (i = 0; i < 8; i++) { + rc = qpnp_adc_tm_reg_update(chip, + QPNP_BTM_Mn_EN(i), + QPNP_BTM_Mn_MEAS_EN, false); + if (rc < 0) + pr_err("multi meas disable failed\n"); + } + } else { + /* Check if a conversion is in progress */ + rc = qpnp_adc_tm_req_sts_check(chip); + if (rc < 0) + pr_err("adc-tm req_sts check failed\n"); - /* Disable multimeasurement */ - reg_val = 0; - rc = qpnp_adc_tm_write_reg(chip, QPNP_ADC_TM_MULTI_MEAS_EN, reg_val, 1); - if (rc < 0) - pr_err("adc-tm multi-measurement mode disable failed\n"); + /* Disable multimeasurement */ + reg_val = 0; + rc = qpnp_adc_tm_write_reg(chip, + QPNP_ADC_TM_MULTI_MEAS_EN, reg_val, 1); + if (rc < 0) + pr_err("adc-tm multi-meas mode disable failed\n"); - rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS1, &status1, 1); - if (rc < 0) - pr_err("adc-tm status1 read failed\n"); + rc = qpnp_adc_tm_read_reg(chip, + QPNP_ADC_TM_STATUS1, &status1, 1); + if (rc < 0) + pr_err("adc-tm status1 read failed\n"); - rc = qpnp_adc_tm_read_reg(chip, QPNP_EN_CTL1, &en_ctl1, 1); - if (rc < 0) - pr_err("adc-tm en_ctl1 read failed\n"); + rc = qpnp_adc_tm_read_reg(chip, + QPNP_EN_CTL1, &en_ctl1, 1); + if (rc < 0) + pr_err("adc-tm en_ctl1 read failed\n"); - pr_debug("adc-tm status1=0%x, en_ctl1=0x%x\n", status1, en_ctl1); + pr_debug("status1=0%x, en_ctl1=0x%x\n", status1, en_ctl1); + } } static int qpnp_adc_tm_suspend_noirq(struct device *dev) diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 807d80145686..96aa0ad32497 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -216,16 +216,11 @@ static int pty_signal(struct tty_struct *tty, int sig) static void pty_flush_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; - struct tty_ldisc *ld; if (!to) return; - ld = tty_ldisc_ref(to); - tty_buffer_flush(to, ld); - if (ld) - tty_ldisc_deref(ld); - + tty_buffer_flush(to, NULL); if (to->packet) { spin_lock_irq(&tty->ctrl_lock); tty->ctrl_status |= TIOCPKT_FLUSHWRITE; diff --git a/drivers/tty/serial/efm32-uart.c b/drivers/tty/serial/efm32-uart.c index 195acc868763..5d476916191b 100644 --- a/drivers/tty/serial/efm32-uart.c +++ b/drivers/tty/serial/efm32-uart.c @@ -27,6 +27,7 @@ #define UARTn_FRAME 0x04 #define UARTn_FRAME_DATABITS__MASK 0x000f #define UARTn_FRAME_DATABITS(n) ((n) - 3) +#define UARTn_FRAME_PARITY__MASK 0x0300 #define UARTn_FRAME_PARITY_NONE 0x0000 #define UARTn_FRAME_PARITY_EVEN 0x0200 #define UARTn_FRAME_PARITY_ODD 0x0300 @@ -572,12 +573,16 @@ static void efm32_uart_console_get_options(struct efm32_uart_port *efm_port, 16 * (4 + (clkdiv >> 6))); frame = efm32_uart_read32(efm_port, UARTn_FRAME); - if (frame & UARTn_FRAME_PARITY_ODD) + switch (frame & UARTn_FRAME_PARITY__MASK) { + case UARTn_FRAME_PARITY_ODD: *parity = 'o'; - else if (frame & UARTn_FRAME_PARITY_EVEN) + break; + case UARTn_FRAME_PARITY_EVEN: *parity = 'e'; - else + break; + default: *parity = 'n'; + } *bits = (frame & UARTn_FRAME_DATABITS__MASK) - UARTn_FRAME_DATABITS(4) + 4; diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 88246f7e435a..0f23dda60011 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -1378,9 +1378,9 @@ static struct spi_driver ifx_spi_driver = { static void __exit ifx_spi_exit(void) { /* unregister */ + spi_unregister_driver(&ifx_spi_driver); tty_unregister_driver(tty_drv); put_tty_driver(tty_drv); - spi_unregister_driver(&ifx_spi_driver); unregister_reboot_notifier(&ifx_modem_reboot_notifier_block); } diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 24280d9a05e9..de1c143b475f 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1712,7 +1712,8 @@ static int serial_omap_probe(struct platform_device *pdev) return 0; err_add_port: - pm_runtime_put(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_qos_remove_request(&up->pm_qos_request); device_init_wakeup(up->dev, false); @@ -1725,9 +1726,13 @@ static int serial_omap_remove(struct platform_device *dev) { struct uart_omap_port *up = platform_get_drvdata(dev); + pm_runtime_get_sync(up->dev); + + uart_remove_one_port(&serial_omap_reg, &up->port); + + pm_runtime_dont_use_autosuspend(up->dev); pm_runtime_put_sync(up->dev); pm_runtime_disable(up->dev); - uart_remove_one_port(&serial_omap_reg, &up->port); pm_qos_remove_request(&up->pm_qos_request); device_init_wakeup(&dev->dev, false); diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index cad76b1cf672..df642496f989 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -900,14 +900,13 @@ static int s3c24xx_serial_request_dma(struct s3c24xx_uart_port *p) return -ENOMEM; } - dma->rx_addr = dma_map_single(dma->rx_chan->device->dev, dma->rx_buf, + dma->rx_addr = dma_map_single(p->port.dev, dma->rx_buf, dma->rx_size, DMA_FROM_DEVICE); spin_lock_irqsave(&p->port.lock, flags); /* TX buffer */ - dma->tx_addr = dma_map_single(dma->tx_chan->device->dev, - p->port.state->xmit.buf, + dma->tx_addr = dma_map_single(p->port.dev, p->port.state->xmit.buf, UART_XMIT_SIZE, DMA_TO_DEVICE); spin_unlock_irqrestore(&p->port.lock, flags); @@ -921,7 +920,7 @@ static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p) if (dma->rx_chan) { dmaengine_terminate_all(dma->rx_chan); - dma_unmap_single(dma->rx_chan->device->dev, dma->rx_addr, + dma_unmap_single(p->port.dev, dma->rx_addr, dma->rx_size, DMA_FROM_DEVICE); kfree(dma->rx_buf); dma_release_channel(dma->rx_chan); @@ -930,7 +929,7 @@ static void s3c24xx_serial_release_dma(struct s3c24xx_uart_port *p) if (dma->tx_chan) { dmaengine_terminate_all(dma->tx_chan); - dma_unmap_single(dma->tx_chan->device->dev, dma->tx_addr, + dma_unmap_single(p->port.dev, dma->tx_addr, UART_XMIT_SIZE, DMA_TO_DEVICE); dma_release_channel(dma->tx_chan); dma->tx_chan = NULL; diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 63a06ab6ba03..235e150d7b81 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1800,11 +1800,13 @@ static int sci_startup(struct uart_port *port) dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); + sci_request_dma(port); + ret = sci_request_irq(s); - if (unlikely(ret < 0)) + if (unlikely(ret < 0)) { + sci_free_dma(port); return ret; - - sci_request_dma(port); + } spin_lock_irqsave(&port->lock, flags); sci_start_tx(port); @@ -1834,8 +1836,8 @@ static void sci_shutdown(struct uart_port *port) } #endif - sci_free_dma(port); sci_free_irq(s); + sci_free_dma(port); } static unsigned int sci_scbrr_calc(struct sci_port *s, unsigned int bps, diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 7cef54334b12..1bb629ab8ecc 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2070,13 +2070,12 @@ retry_open: if (tty) { mutex_unlock(&tty_mutex); retval = tty_lock_interruptible(tty); + tty_kref_put(tty); /* drop kref from tty_driver_lookup_tty() */ if (retval) { if (retval == -EINTR) retval = -ERESTARTSYS; goto err_unref; } - /* safe to drop the kref from tty_driver_lookup_tty() */ - tty_kref_put(tty); retval = tty_reopen(tty); if (retval < 0) { tty_unlock(tty); diff --git a/drivers/tty/tty_mutex.c b/drivers/tty/tty_mutex.c index d09293bc0e04..cff304abb619 100644 --- a/drivers/tty/tty_mutex.c +++ b/drivers/tty/tty_mutex.c @@ -24,10 +24,15 @@ EXPORT_SYMBOL(tty_lock); int tty_lock_interruptible(struct tty_struct *tty) { + int ret; + if (WARN(tty->magic != TTY_MAGIC, "L Bad %p\n", tty)) return -EIO; tty_kref_get(tty); - return mutex_lock_interruptible(&tty->legacy_mutex); + ret = mutex_lock_interruptible(&tty->legacy_mutex); + if (ret) + tty_kref_put(tty); + return ret; } void __lockfunc tty_unlock(struct tty_struct *tty) diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c index 58c8485a0715..923379972707 100644 --- a/drivers/usb/chipidea/debug.c +++ b/drivers/usb/chipidea/debug.c @@ -295,7 +295,8 @@ static int ci_role_show(struct seq_file *s, void *data) { struct ci_hdrc *ci = s->private; - seq_printf(s, "%s\n", ci_role(ci)->name); + if (ci->role != CI_ROLE_END) + seq_printf(s, "%s\n", ci_role(ci)->name); return 0; } diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index d8a045fc1fdb..aff086ca97e4 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1982,6 +1982,7 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci) int ci_hdrc_gadget_init(struct ci_hdrc *ci) { struct ci_role_driver *rdrv; + int ret; if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC)) return -ENXIO; @@ -1994,7 +1995,10 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci) rdrv->stop = udc_id_switch_for_host; rdrv->irq = udc_irq; rdrv->name = "gadget"; - ci->roles[CI_ROLE_GADGET] = rdrv; - return udc_start(ci); + ret = udc_start(ci); + if (!ret) + ci->roles[CI_ROLE_GADGET] = rdrv; + + return ret; } diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 96849e2e7435..0b7194086c5a 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -311,6 +311,12 @@ static void acm_ctrl_irq(struct urb *urb) break; case USB_CDC_NOTIFY_SERIAL_STATE: + if (le16_to_cpu(dr->wLength) != 2) { + dev_dbg(&acm->control->dev, + "%s - malformed serial state\n", __func__); + break; + } + newctrl = get_unaligned_le16(data); if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { @@ -347,11 +353,10 @@ static void acm_ctrl_irq(struct urb *urb) default: dev_dbg(&acm->control->dev, - "%s - unknown notification %d received: index %d " - "len %d data0 %d data1 %d\n", + "%s - unknown notification %d received: index %d len %d\n", __func__, - dr->bNotificationType, dr->wIndex, - dr->wLength, data[0], data[1]); + dr->bNotificationType, dr->wIndex, dr->wLength); + break; } exit: diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 325cbc9c35d8..b1298f093f13 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -949,6 +949,14 @@ int usb_get_bos_descriptor(struct usb_device *dev) dev->bos->ss_id = (struct usb_ss_container_id_descriptor *)buffer; break; + case USB_CAP_TYPE_CONFIG_SUMMARY: + /* one such desc per configuration */ + if (!dev->bos->num_config_summary_desc) + dev->bos->config_summary = + (struct usb_config_summary_descriptor *)buffer; + + dev->bos->num_config_summary_desc++; + break; default: break; } diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 26a305f43ff6..ee33c0d796b5 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1328,6 +1328,24 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) */ if (udev->parent && !PMSG_IS_AUTO(msg)) status = 0; + + /* + * If the device is inaccessible, don't try to resume + * suspended interfaces and just return the error. + */ + if (status && status != -EBUSY) { + int err; + u16 devstat; + + err = usb_get_status(udev, USB_RECIP_DEVICE, 0, + &devstat); + if (err) { + dev_err(&udev->dev, + "Failed to suspend device, error %d\n", + status); + goto done; + } + } } /* If the suspend failed, resume interfaces that did get suspended */ @@ -1772,6 +1790,9 @@ static int autosuspend_check(struct usb_device *udev) int w, i; struct usb_interface *intf; + if (udev->state == USB_STATE_NOTATTACHED) + return -ENODEV; + /* Fail if autosuspend is disabled, or any interfaces are in use, or * any interface drivers require remote wakeup but it isn't available. */ diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index ea337a718cc1..b3de806085f0 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -26,6 +26,7 @@ #define MAX_USB_MINORS 256 static const struct file_operations *usb_minors[MAX_USB_MINORS]; static DECLARE_RWSEM(minor_rwsem); +static DEFINE_MUTEX(init_usb_class_mutex); static int usb_open(struct inode *inode, struct file *file) { @@ -108,8 +109,9 @@ static void release_usb_class(struct kref *kref) static void destroy_usb_class(void) { - if (usb_class) - kref_put(&usb_class->kref, release_usb_class); + mutex_lock(&init_usb_class_mutex); + kref_put(&usb_class->kref, release_usb_class); + mutex_unlock(&init_usb_class_mutex); } int usb_major_init(void) @@ -171,7 +173,10 @@ int usb_register_dev(struct usb_interface *intf, if (intf->minor >= 0) return -EADDRINUSE; + mutex_lock(&init_usb_class_mutex); retval = init_usb_class(); + mutex_unlock(&init_usb_class_mutex); + if (retval) return retval; diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 358ca8dd784f..72d1109f13eb 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -19,6 +19,8 @@ #include <linux/usb.h> #include <linux/usb/hcd.h> +#include <linux/usb/audio.h> +#include <linux/usb/audio-v3.h> #include "usb.h" static inline const char *plural(int n) @@ -40,6 +42,36 @@ static int is_activesync(struct usb_interface_descriptor *desc) && desc->bInterfaceProtocol == 1; } +static int usb_audio_max_rev_config(struct usb_host_bos *bos) +{ + int desc_cnt, func_cnt, numfunc; + int num_cfg_desc; + struct usb_config_summary_descriptor *conf_summary; + + if (!bos || !bos->config_summary) + goto done; + + conf_summary = bos->config_summary; + num_cfg_desc = bos->num_config_summary_desc; + + for (desc_cnt = 0; desc_cnt < num_cfg_desc; desc_cnt++) { + numfunc = conf_summary->bNumFunctions; + for (func_cnt = 0; func_cnt < numfunc; func_cnt++) { + /* look for BADD 3.0 */ + if (conf_summary->cs_info[func_cnt].bClass == + USB_CLASS_AUDIO && + conf_summary->cs_info[func_cnt].bProtocol == + UAC_VERSION_3 && + conf_summary->cs_info[func_cnt].bSubClass != + FULL_ADC_PROFILE) + return conf_summary->bConfigurationValue; + } + } + +done: + return -EINVAL; +} + int usb_choose_configuration(struct usb_device *udev) { int i; @@ -130,7 +162,6 @@ int usb_choose_configuration(struct usb_device *udev) best = c; break; } - /* If all the remaining configs are vendor-specific, * choose the first one. */ else if (!best) @@ -143,7 +174,10 @@ int usb_choose_configuration(struct usb_device *udev) insufficient_power, plural(insufficient_power)); if (best) { - i = best->desc.bConfigurationValue; + /* choose usb audio class preferred config if available */ + i = usb_audio_max_rev_config(udev->bos); + if (i < 0) + i = best->desc.bConfigurationValue; dev_dbg(&udev->dev, "configuration #%d chosen from %d choice%s\n", i, num_configs, plural(num_configs)); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index aa00bb51940b..bc5a6966dda9 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2569,6 +2569,7 @@ struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex), GFP_KERNEL); if (!hcd->bandwidth_mutex) { + kfree(hcd->address0_mutex); kfree(hcd); dev_dbg(dev, "hcd bandwidth mutex alloc failed\n"); return NULL; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 87912ead87b7..5de22f4892cd 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -363,7 +363,8 @@ static void usb_set_lpm_parameters(struct usb_device *udev) } /* USB 2.0 spec Section 11.24.4.5 */ -static int get_hub_descriptor(struct usb_device *hdev, void *data) +static int get_hub_descriptor(struct usb_device *hdev, + struct usb_hub_descriptor *desc) { int i, ret, size; unsigned dtype; @@ -379,10 +380,18 @@ static int get_hub_descriptor(struct usb_device *hdev, void *data) for (i = 0; i < 3; i++) { ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, - dtype << 8, 0, data, size, + dtype << 8, 0, desc, size, USB_CTRL_GET_TIMEOUT); - if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2)) + if (hub_is_superspeed(hdev)) { + if (ret == size) + return ret; + } else if (ret >= USB_DT_HUB_NONVAR_SIZE + 2) { + /* Make sure we have the DeviceRemovable field. */ + size = USB_DT_HUB_NONVAR_SIZE + desc->bNbrPorts / 8 + 1; + if (ret < size) + return -EMSGSIZE; return ret; + } } return -EINVAL; } @@ -1059,6 +1068,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) portstatus = portchange = 0; status = hub_port_status(hub, port1, &portstatus, &portchange); + if (status) + goto abort; + if (udev || (portstatus & USB_PORT_STAT_CONNECTION)) dev_dbg(&port_dev->dev, "status %04x change %04x\n", portstatus, portchange); @@ -1191,7 +1203,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) /* Scan all ports that need attention */ kick_hub_wq(hub); - + abort: if (type == HUB_INIT2 || type == HUB_INIT3) { /* Allow autosuspend if it was suppressed */ disconnected: @@ -1303,7 +1315,7 @@ static int hub_configure(struct usb_hub *hub, } mutex_init(&hub->status_mutex); - hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL); + hub->descriptor = kzalloc(sizeof(*hub->descriptor), GFP_KERNEL); if (!hub->descriptor) { ret = -ENOMEM; goto fail; @@ -1311,13 +1323,19 @@ static int hub_configure(struct usb_hub *hub, /* Request the entire hub descriptor. * hub->descriptor can handle USB_MAXCHILDREN ports, - * but the hub can/will return fewer bytes here. + * but a (non-SS) hub can/will return fewer bytes here. */ ret = get_hub_descriptor(hdev, hub->descriptor); if (ret < 0) { message = "can't read hub descriptor"; goto fail; - } else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) { + } + + maxchild = USB_MAXCHILDREN; + if (hub_is_superspeed(hdev)) + maxchild = min_t(unsigned, maxchild, USB_SS_MAXPORTS); + + if (hub->descriptor->bNbrPorts > maxchild) { message = "hub has too many ports!"; ret = -ENODEV; goto fail; @@ -2079,6 +2097,12 @@ void usb_disconnect(struct usb_device **pdev) dev_info(&udev->dev, "USB disconnect, device number %d\n", udev->devnum); + /* + * Ensure that the pm runtime code knows that the USB device + * is in the process of being disconnected. + */ + pm_runtime_barrier(&udev->dev); + usb_lock_device(udev); hub_disconnect_children(udev); diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 2f1fb7e7aa54..9eba51b92f72 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -148,7 +148,8 @@ static int dwc3_exynos_probe(struct platform_device *pdev) exynos->axius_clk = devm_clk_get(dev, "usbdrd30_axius_clk"); if (IS_ERR(exynos->axius_clk)) { dev_err(dev, "no AXI UpScaler clk specified\n"); - return -ENODEV; + ret = -ENODEV; + goto axius_clk_err; } clk_prepare_enable(exynos->axius_clk); } else { @@ -206,6 +207,7 @@ err3: regulator_disable(exynos->vdd33); err2: clk_disable_unprepare(exynos->axius_clk); +axius_clk_err: clk_disable_unprepare(exynos->susp_clk); clk_disable_unprepare(exynos->clk); return ret; diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 4ed46d9ca279..c2d788bc4bc5 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -2671,12 +2671,16 @@ static int dwc3_msm_extcon_register(struct dwc3_msm *mdwc) { struct device_node *node = mdwc->dev->of_node; struct extcon_dev *edev; + struct dwc3 *dwc; int ret = 0; + dwc = platform_get_drvdata(mdwc->dwc3); if (!of_property_read_bool(node, "extcon")) { - if (usb_get_dr_mode(&mdwc->dwc3->dev) == USB_DR_MODE_HOST) + dev_dbg(mdwc->dev, "extcon property doesn't exist\n"); + if (usb_get_dr_mode(&mdwc->dwc3->dev) == USB_DR_MODE_HOST + || dwc->is_drd) return 0; - dev_err(mdwc->dev, "extcon property doesn't exist\n"); + dev_err(mdwc->dev, "Neither host nor DRD, fail probe\n"); return -EINVAL; } @@ -3137,8 +3141,9 @@ static int dwc3_msm_probe(struct platform_device *pdev) device_create_file(&pdev->dev, &dev_attr_speed); host_mode = usb_get_dr_mode(&mdwc->dwc3->dev) == USB_DR_MODE_HOST; - if (!dwc->is_drd && host_mode) { - dev_dbg(&pdev->dev, "DWC3 in host only mode\n"); + if (host_mode || + (dwc->is_drd && !of_property_read_bool(node, "extcon"))) { + dev_dbg(&pdev->dev, "DWC3 in default host mode\n"); mdwc->id_state = DWC3_ID_GROUND; dwc3_ext_event_notify(mdwc); } diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index e5d3a0bdf32a..a2c14bb5efa4 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -23,6 +23,7 @@ void acc_disconnect(void); static struct class *android_class; static struct device *android_device; static int index; +static int gadget_index; struct device *create_function_device(char *name) { @@ -1439,21 +1440,21 @@ static void android_work(struct work_struct *data) spin_unlock_irqrestore(&cdev->lock, flags); if (status[0]) { - kobject_uevent_env(&android_device->kobj, + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, connected); pr_info("%s: sent uevent %s\n", __func__, connected[0]); uevent_sent = true; } if (status[1]) { - kobject_uevent_env(&android_device->kobj, + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, configured); pr_info("%s: sent uevent %s\n", __func__, configured[0]); uevent_sent = true; } if (status[2]) { - kobject_uevent_env(&android_device->kobj, + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, disconnected); pr_info("%s: sent uevent %s\n", __func__, disconnected[0]); uevent_sent = true; @@ -1613,23 +1614,28 @@ static int android_device_create(struct gadget_info *gi) { struct device_attribute **attrs; struct device_attribute *attr; + char str[10]; INIT_WORK(&gi->work, android_work); - android_device = device_create(android_class, NULL, - MKDEV(0, 0), NULL, "android0"); - if (IS_ERR(android_device)) - return PTR_ERR(android_device); + snprintf(str, sizeof(str), "android%d", gadget_index - 1); + pr_debug("Creating android device %s\n", str); + gi->dev = device_create(android_class, NULL, + MKDEV(0, 0), NULL, str); + if (IS_ERR(gi->dev)) + return PTR_ERR(gi->dev); - dev_set_drvdata(android_device, gi); + dev_set_drvdata(gi->dev, gi); + if (gadget_index == 1) + android_device = gi->dev; attrs = android_usb_attributes; while ((attr = *attrs++)) { int err; - err = device_create_file(android_device, attr); + err = device_create_file(gi->dev, attr); if (err) { - device_destroy(android_device->class, - android_device->devt); + device_destroy(gi->dev->class, + gi->dev->devt); return err; } } @@ -1637,15 +1643,15 @@ static int android_device_create(struct gadget_info *gi) return 0; } -static void android_device_destroy(void) +static void android_device_destroy(struct device *dev) { struct device_attribute **attrs; struct device_attribute *attr; attrs = android_usb_attributes; while ((attr = *attrs++)) - device_remove_file(android_device, attr); - device_destroy(android_device->class, android_device->devt); + device_remove_file(dev, attr); + device_destroy(dev->class, dev->devt); } #else static inline int android_device_create(struct gadget_info *gi) @@ -1653,7 +1659,7 @@ static inline int android_device_create(struct gadget_info *gi) return 0; } -static inline void android_device_destroy(void) +static inline void android_device_destroy(struct device *dev) { } #endif @@ -1705,6 +1711,8 @@ static struct config_group *gadgets_make( if (!gi->composite.gadget_driver.function) goto err; + gadget_index++; + pr_debug("Creating gadget index %d\n", gadget_index); if (android_device_create(gi) < 0) goto err; @@ -1719,8 +1727,14 @@ err: static void gadgets_drop(struct config_group *group, struct config_item *item) { + struct gadget_info *gi; + + gi = container_of(to_config_group(item), struct gadget_info, group); config_item_put(item); - android_device_destroy(); + if (gi->dev) { + android_device_destroy(gi->dev); + gi->dev = NULL; + } } static struct configfs_group_operations gadgets_ops = { diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c index 46be62b737f1..ec73b67096c5 100644 --- a/drivers/usb/gadget/function/f_audio_source.c +++ b/drivers/usb/gadget/function/f_audio_source.c @@ -776,11 +776,11 @@ static int audio_pcm_close(struct snd_pcm_substream *substream) struct audio_dev *audio = substream->private_data; unsigned long flags; - spin_lock_irqsave(&audio->lock, flags); - /* Remove the QoS request */ pm_qos_remove_request(&audio->pm_qos); + spin_lock_irqsave(&audio->lock, flags); + audio->substream = NULL; spin_unlock_irqrestore(&audio->lock, flags); diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 7c35241a487a..b6f4790ffc08 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2015,6 +2015,12 @@ static int ffs_func_eps_enable(struct ffs_function *func) break; } + /* + * userspace setting maxburst > 1 results more fifo + * allocation than without maxburst. Change maxburst to 1 + * only to allocate fifo size of max packet size. + */ + ep->ep->maxburst = 1; ret = usb_ep_enable(ep->ep); if (likely(!ret)) { epfile->ep = ep; diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index 7216fdd4245d..3f903d4776b4 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -1035,7 +1035,7 @@ gsi_ctrl_dev_read(struct file *fp, char __user *buf, size_t count, loff_t *pos) log_event_dbg("%s: cpkt size:%d", __func__, cpkt->len); if (qti_packet_debug) print_hex_dump(KERN_DEBUG, "READ:", DUMP_PREFIX_OFFSET, 16, 1, - buf, min_t(int, 30, cpkt->len), false); + cpkt->buf, min_t(int, 30, cpkt->len), false); ret = copy_to_user(buf, cpkt->buf, cpkt->len); if (ret) { @@ -1108,7 +1108,7 @@ static ssize_t gsi_ctrl_dev_write(struct file *fp, const char __user *buf, c_port->copied_from_modem++; if (qti_packet_debug) print_hex_dump(KERN_DEBUG, "WRITE:", DUMP_PREFIX_OFFSET, 16, 1, - buf, min_t(int, 30, count), false); + cpkt->buf, min_t(int, 30, count), false); spin_lock_irqsave(&c_port->lock, flags); list_add_tail(&cpkt->list, &c_port->cpkt_resp_q); diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 59d6ac67d072..9b7274821d0b 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -399,7 +399,11 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) /* Caller must hold fsg->lock */ static void wakeup_thread(struct fsg_common *common) { - smp_wmb(); /* ensure the write of bh->state is complete */ + /* + * Ensure the reading of thread_wakeup_needed + * and the writing of bh->state are completed + */ + smp_mb(); /* Tell the main thread that something has happened */ common->thread_wakeup_needed = 1; if (common->thread_task) @@ -649,7 +653,12 @@ static int sleep_thread(struct fsg_common *common, bool can_freeze) } __set_current_state(TASK_RUNNING); common->thread_wakeup_needed = 0; - smp_rmb(); /* ensure the latest bh->state is visible */ + + /* + * Ensure the writing of thread_wakeup_needed + * and the reading of bh->state are completed + */ + smp_mb(); return rc; } diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c index 999433ae2d72..d8cc5fd39e85 100644 --- a/drivers/usb/gadget/function/f_mtp.c +++ b/drivers/usb/gadget/function/f_mtp.c @@ -1012,6 +1012,10 @@ static void receive_file_work(struct work_struct *data) usb_ep_dequeue(dev->ep_out, read_req); break; } + if (read_req->status) { + r = read_req->status; + break; + } mutex_lock(&dev->read_mutex); if (dev->state == STATE_OFFLINE) { diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index de014436fb22..43ce2cfcdb4d 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1676,9 +1676,10 @@ static void gadgetfs_suspend (struct usb_gadget *gadget) { struct dev_data *dev = get_gadget_data (gadget); + unsigned long flags; INFO (dev, "suspended from state %d\n", dev->state); - spin_lock (&dev->lock); + spin_lock_irqsave(&dev->lock, flags); switch (dev->state) { case STATE_DEV_SETUP: // VERY odd... host died?? case STATE_DEV_CONNECTED: @@ -1689,7 +1690,7 @@ gadgetfs_suspend (struct usb_gadget *gadget) default: break; } - spin_unlock (&dev->lock); + spin_unlock_irqrestore(&dev->lock, flags); } static struct usb_gadget_driver gadgetfs_driver = { diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 6610f7a023d3..64f404a1a072 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -442,23 +442,16 @@ static void set_link_state(struct dummy_hcd *dum_hcd) /* Report reset and disconnect events to the driver */ if (dum->driver && (disconnect || reset)) { stop_activity(dum); - spin_unlock(&dum->lock); if (reset) usb_gadget_udc_reset(&dum->gadget, dum->driver); else dum->driver->disconnect(&dum->gadget); - spin_lock(&dum->lock); } } else if (dum_hcd->active != dum_hcd->old_active) { - if (dum_hcd->old_active && dum->driver->suspend) { - spin_unlock(&dum->lock); + if (dum_hcd->old_active && dum->driver->suspend) dum->driver->suspend(&dum->gadget); - spin_lock(&dum->lock); - } else if (!dum_hcd->old_active && dum->driver->resume) { - spin_unlock(&dum->lock); + else if (!dum_hcd->old_active && dum->driver->resume) dum->driver->resume(&dum->gadget); - spin_lock(&dum->lock); - } } dum_hcd->old_status = dum_hcd->port_status; @@ -985,7 +978,9 @@ static int dummy_udc_stop(struct usb_gadget *g) struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g); struct dummy *dum = dum_hcd->dum; + spin_lock_irq(&dum->lock); dum->driver = NULL; + spin_unlock_irq(&dum->lock); return 0; } @@ -2011,7 +2006,7 @@ ss_hub_descriptor(struct usb_hub_descriptor *desc) HUB_CHAR_COMMON_OCPM); desc->bNbrPorts = 1; desc->u.ss.bHubHdrDecLat = 0x04; /* Worst case: 0.4 micro sec*/ - desc->u.ss.DeviceRemovable = 0xffff; + desc->u.ss.DeviceRemovable = 0; } static inline void hub_descriptor(struct usb_hub_descriptor *desc) @@ -2023,8 +2018,8 @@ static inline void hub_descriptor(struct usb_hub_descriptor *desc) HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM); desc->bNbrPorts = 1; - desc->u.hs.DeviceRemovable[0] = 0xff; - desc->u.hs.DeviceRemovable[1] = 0xff; + desc->u.hs.DeviceRemovable[0] = 0; + desc->u.hs.DeviceRemovable[1] = 0xff; /* PortPwrCtrlMask */ } static int dummy_hub_control( diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 6706aef907f4..a47de8c31ce9 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -2425,11 +2425,8 @@ static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver) nuke(&dev->ep[i]); /* report disconnect; the driver is already quiesced */ - if (driver) { - spin_unlock(&dev->lock); + if (driver) driver->disconnect(&dev->gadget); - spin_lock(&dev->lock); - } usb_reinit(dev); } @@ -3275,8 +3272,6 @@ next_endpoints: BIT(PCI_RETRY_ABORT_INTERRUPT)) static void handle_stat1_irqs(struct net2280 *dev, u32 stat) -__releases(dev->lock) -__acquires(dev->lock) { struct net2280_ep *ep; u32 tmp, num, mask, scratch; @@ -3317,14 +3312,12 @@ __acquires(dev->lock) if (disconnect || reset) { stop_activity(dev, dev->driver); ep0_start(dev); - spin_unlock(&dev->lock); if (reset) usb_gadget_udc_reset (&dev->gadget, dev->driver); else (dev->driver->disconnect) (&dev->gadget); - spin_lock(&dev->lock); return; } } diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 4cbd0633c5c2..a11c2c8bda53 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -1269,7 +1269,7 @@ static void set_td_timer(struct r8a66597 *r8a66597, struct r8a66597_td *td) time = 30; break; default: - time = 300; + time = 50; break; } @@ -1785,6 +1785,7 @@ static void r8a66597_td_timer(unsigned long _r8a66597) pipe = td->pipe; pipe_stop(r8a66597, pipe); + /* Select a different address or endpoint */ new_td = td; do { list_move_tail(&new_td->queue, @@ -1794,7 +1795,8 @@ static void r8a66597_td_timer(unsigned long _r8a66597) new_td = td; break; } - } while (td != new_td && td->address == new_td->address); + } while (td != new_td && td->address == new_td->address && + td->pipe->info.epnum == new_td->pipe->info.epnum); start_transfer(r8a66597, new_td); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index d885033d3322..9dbd7595a7a3 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -376,10 +376,6 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) int i; ret = 0; - virt_dev = xhci->devs[slot_id]; - if (!virt_dev) - return -ENODEV; - cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO); if (!cmd) { xhci_dbg(xhci, "Couldn't allocate command structure.\n"); @@ -387,6 +383,13 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) } spin_lock_irqsave(&xhci->lock, flags); + virt_dev = xhci->devs[slot_id]; + if (!virt_dev) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_free_command(xhci, cmd); + return -ENODEV; + } + for (i = LAST_EP_INDEX; i > 0; i--) { if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) { struct xhci_command *command; @@ -403,6 +406,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) i, suspend); if (ret) { spin_unlock_irqrestore(&xhci->lock, flags); + xhci_free_command(xhci, command); goto err_cmd_queue; } } diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 82483599a882..35e0c046fdcc 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1711,7 +1711,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma); for (i = 0; i < num_sp; i++) { dma_addr_t dma; - void *buf = dma_alloc_coherent(dev, xhci->page_size, &dma, + void *buf = dma_zalloc_coherent(dev, xhci->page_size, &dma, flags); if (!buf) goto fail_sp5; @@ -2768,7 +2768,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) (xhci->cmd_ring->first_seg->dma & (u64) ~CMD_RING_RSVD_BITS) | xhci->cmd_ring->cycle_state; xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Setting command ring address to 0x%x", val); + "// Setting command ring address to 0x%016llx", val_64); xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring); xhci_dbg_cmd_ptrs(xhci); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index dd262f418140..e8f990642281 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -52,6 +52,7 @@ #define PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI 0x0aa8 #define PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI 0x1aa8 #define PCI_DEVICE_ID_INTEL_APL_XHCI 0x5aa8 +#define PCI_DEVICE_ID_INTEL_DNV_XHCI 0x19d0 static const char hcd_name[] = "xhci_hcd"; @@ -167,12 +168,14 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_M_XHCI || pdev->device == PCI_DEVICE_ID_INTEL_BROXTON_B_XHCI || - pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) { + pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI)) { xhci->quirks |= XHCI_PME_STUCK_QUIRK; } if (pdev->vendor == PCI_VENDOR_ID_INTEL && (pdev->device == PCI_DEVICE_ID_INTEL_CHERRYVIEW_XHCI || - pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI)) + pdev->device == PCI_DEVICE_ID_INTEL_APL_XHCI || + pdev->device == PCI_DEVICE_ID_INTEL_DNV_XHCI)) xhci->quirks |= XHCI_MISSING_CAS; if (pdev->vendor == PCI_VENDOR_ID_ETRON && @@ -195,6 +198,9 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && pdev->device == 0x1042) xhci->quirks |= XHCI_BROKEN_STREAMS; + if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && + pdev->device == 0x1142) + xhci->quirks |= XHCI_TRUST_TX_LENGTH; if (xhci->quirks & XHCI_RESET_ON_RESUME) xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 1ddf882fb607..56a9cd62f2c4 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -154,7 +154,7 @@ static int xhci_plat_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) - return -ENODEV; + return irq; /* Try to set 64-bit DMA first */ if (WARN_ON(!pdev->dev.dma_mask)) diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 775690bed4c0..5e43fd881a9c 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -557,7 +557,7 @@ static long iowarrior_ioctl(struct file *file, unsigned int cmd, info.revision = le16_to_cpu(dev->udev->descriptor.bcdDevice); /* 0==UNKNOWN, 1==LOW(usb1.1) ,2=FULL(usb1.1), 3=HIGH(usb2.0) */ - info.speed = le16_to_cpu(dev->udev->speed); + info.speed = dev->udev->speed; info.if_num = dev->interface->cur_altsetting->desc.bInterfaceNumber; info.report_size = dev->report_size; diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 4dd531ac5a7f..0ec9ee573ffa 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -317,9 +317,16 @@ static int tower_open (struct inode *inode, struct file *file) int subminor; int retval = 0; struct usb_interface *interface; - struct tower_reset_reply reset_reply; + struct tower_reset_reply *reset_reply; int result; + reset_reply = kmalloc(sizeof(*reset_reply), GFP_KERNEL); + + if (!reset_reply) { + retval = -ENOMEM; + goto exit; + } + nonseekable_open(inode, file); subminor = iminor(inode); @@ -364,8 +371,8 @@ static int tower_open (struct inode *inode, struct file *file) USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, 0, 0, - &reset_reply, - sizeof(reset_reply), + reset_reply, + sizeof(*reset_reply), 1000); if (result < 0) { dev_err(&dev->udev->dev, @@ -406,6 +413,7 @@ unlock_exit: mutex_unlock(&dev->lock); exit: + kfree(reset_reply); return retval; } @@ -808,7 +816,7 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device struct lego_usb_tower *dev = NULL; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor* endpoint; - struct tower_get_version_reply get_version_reply; + struct tower_get_version_reply *get_version_reply = NULL; int i; int retval = -ENOMEM; int result; @@ -898,6 +906,13 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device dev->interrupt_in_interval = interrupt_in_interval ? interrupt_in_interval : dev->interrupt_in_endpoint->bInterval; dev->interrupt_out_interval = interrupt_out_interval ? interrupt_out_interval : dev->interrupt_out_endpoint->bInterval; + get_version_reply = kmalloc(sizeof(*get_version_reply), GFP_KERNEL); + + if (!get_version_reply) { + retval = -ENOMEM; + goto error; + } + /* get the firmware version and log it */ result = usb_control_msg (udev, usb_rcvctrlpipe(udev, 0), @@ -905,18 +920,19 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, 0, 0, - &get_version_reply, - sizeof(get_version_reply), + get_version_reply, + sizeof(*get_version_reply), 1000); if (result < 0) { dev_err(idev, "LEGO USB Tower get version control request failed\n"); retval = result; goto error; } - dev_info(&interface->dev, "LEGO USB Tower firmware version is %d.%d " - "build %d\n", get_version_reply.major, - get_version_reply.minor, - le16_to_cpu(get_version_reply.build_no)); + dev_info(&interface->dev, + "LEGO USB Tower firmware version is %d.%d build %d\n", + get_version_reply->major, + get_version_reply->minor, + le16_to_cpu(get_version_reply->build_no)); /* we can register the device now, as it is ready */ usb_set_intfdata (interface, dev); @@ -937,9 +953,11 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device USB_MAJOR, dev->minor); exit: + kfree(get_version_reply); return retval; error: + kfree(get_version_reply); tower_delete(dev); return retval; } diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 1624b09d9748..2e947dc94e32 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -135,6 +135,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf) case USB_ENDPOINT_XFER_INT: if (dev->info->intr) goto try_intr; + continue; case USB_ENDPOINT_XFER_ISOC: if (dev->info->iso) goto try_iso; diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index 4c82077da475..6020024cb87c 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -220,6 +220,7 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, u32 dma_remaining; int src_burst, dst_burst; u16 csr; + u32 psize; int ch; s8 dmareq; s8 sync_dev; @@ -391,15 +392,19 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, if (chdat->tx) { /* Send transfer_packet_sz packets at a time */ - musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, - chdat->transfer_packet_sz); + psize = musb_readl(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET); + psize &= ~0x7ff; + psize |= chdat->transfer_packet_sz; + musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, psize); musb_writel(ep_conf, TUSB_EP_TX_OFFSET, TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len)); } else { /* Receive transfer_packet_sz packets at a time */ - musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, - chdat->transfer_packet_sz << 16); + psize = musb_readl(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET); + psize &= ~(0x7ff << 16); + psize |= (chdat->transfer_packet_sz << 16); + musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, psize); musb_writel(ep_conf, TUSB_EP_RX_OFFSET, TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len)); diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 7bfadccf0e47..03aeec2e878c 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -29,6 +29,15 @@ #include <linux/usb/usbpd.h> #include "usbpd.h" +/* To start USB stack for USB3.1 complaince testing */ +static bool usb_compliance_mode; +module_param(usb_compliance_mode, bool, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(usb_compliance_mode, "Start USB stack for USB3.1 compliance testing"); + +static bool disable_usb_pd; +module_param(disable_usb_pd, bool, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(disable_usb_pd, "Disable USB PD for USB3.1 compliance testing"); + enum usbpd_state { PE_UNKNOWN, PE_ERROR_RECOVERY, @@ -187,6 +196,8 @@ static void *usbpd_ipc_log; #define PD_MAX_MSG_ID 7 +#define PD_MAX_DATA_OBJ 7 + #define PD_MSG_HDR(type, dr, pr, id, cnt, rev) \ (((type) & 0xF) | ((dr) << 5) | (rev << 6) | \ ((pr) << 8) | ((id) << 9) | ((cnt) << 12)) @@ -308,7 +319,7 @@ struct usbpd { struct list_head rx_q; spinlock_t rx_lock; - u32 received_pdos[7]; + u32 received_pdos[PD_MAX_DATA_OBJ]; u16 src_cap_id; u8 selected_pdo; u8 requested_pdo; @@ -481,13 +492,12 @@ static int pd_send_msg(struct usbpd *pd, u8 hdr_type, const u32 *data, ret = pd_phy_write(hdr, (u8 *)data, num_data * sizeof(u32), type, 15); /* TODO figure out timeout. based on tReceive=1.1ms x nRetryCount? */ - /* MessageID incremented regardless of Tx error */ - pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID; - if (ret < 0) return ret; else if (ret != num_data * sizeof(u32)) return -EIO; + + pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID; return 0; } @@ -547,6 +557,7 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos, int uv, int ua) static int pd_eval_src_caps(struct usbpd *pd) { + int obj_cnt; union power_supply_propval val; u32 first_pdo = pd->received_pdos[0]; @@ -563,6 +574,13 @@ static int pd_eval_src_caps(struct usbpd *pd) power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, &val); + for (obj_cnt = 1; obj_cnt < PD_MAX_DATA_OBJ; obj_cnt++) { + if ((PD_SRC_PDO_TYPE(pd->received_pdos[obj_cnt]) == + PD_SRC_PDO_TYPE_AUGMENTED) && + !PD_APDO_PPS(pd->received_pdos[obj_cnt])) + pd->spec_rev = USBPD_REV_30; + } + /* Select the first PDO (vSafe5V) immediately. */ pd_select_pdo(pd, 1, 0, 0); @@ -571,6 +589,8 @@ static int pd_eval_src_caps(struct usbpd *pd) static void pd_send_hard_reset(struct usbpd *pd) { + union power_supply_propval val = {0}; + usbpd_dbg(&pd->dev, "send hard reset"); /* Force CC logic to source/sink to keep Rp/Rd unchanged */ @@ -578,6 +598,7 @@ static void pd_send_hard_reset(struct usbpd *pd) pd->hard_reset_count++; pd_phy_signal(HARD_RESET_SIG, 5); /* tHardResetComplete */ pd->in_pr_swap = false; + power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PR_SWAP, &val); } static void kick_sm(struct usbpd *pd, int ms) @@ -593,6 +614,8 @@ static void kick_sm(struct usbpd *pd, int ms) static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) { + union power_supply_propval val = {1}; + if (type != HARD_RESET_SIG) { usbpd_err(&pd->dev, "invalid signal (%d) received\n", type); return; @@ -603,6 +626,9 @@ static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type) /* Force CC logic to source/sink to keep Rp/Rd unchanged */ set_power_role(pd, pd->current_pr); pd->hard_reset_recvd = true; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); + kick_sm(pd, 0); } @@ -653,12 +679,6 @@ static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type, return; } - /* if spec rev differs (i.e. is older), update PHY */ - if (PD_MSG_HDR_REV(header) < pd->spec_rev) { - pd->spec_rev = PD_MSG_HDR_REV(header); - pd_phy_update_spec_rev(pd->spec_rev); - } - rx_msg = kzalloc(sizeof(*rx_msg), GFP_KERNEL); if (!rx_msg) return; @@ -701,7 +721,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) .shutdown_cb = phy_shutdown, .frame_filter_val = FRAME_FILTER_EN_SOP | FRAME_FILTER_EN_HARD_RESET, - .spec_rev = USBPD_REV_20, }; union power_supply_propval val = {0}; unsigned long flags; @@ -748,8 +767,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val); - /* support only PD 2.0 as a source */ - pd->spec_rev = USBPD_REV_20; pd_reset_protocol(pd); if (!pd->in_pr_swap) { @@ -760,7 +777,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) phy_params.data_role = pd->current_dr; phy_params.power_role = pd->current_pr; - phy_params.spec_rev = pd->spec_rev; ret = pd_phy_open(&phy_params); if (ret) { @@ -772,14 +788,15 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) } pd->pd_phy_opened = true; - } else { - pd_phy_update_spec_rev(pd->spec_rev); } pd->current_state = PE_SRC_SEND_CAPABILITIES; if (pd->in_pr_swap) { kick_sm(pd, SWAP_SOURCE_START_TIME); pd->in_pr_swap = false; + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); break; } @@ -892,7 +909,8 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) pd->current_dr = DR_UFP; if (pd->psy_type == POWER_SUPPLY_TYPE_USB || - pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP) + pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP || + usb_compliance_mode) start_usb_peripheral(pd); } @@ -906,14 +924,9 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) break; } - if (!val.intval) + if (!val.intval || disable_usb_pd) break; - /* - * support up to PD 3.0 as a sink; if source is 2.0, - * phy_msg_received() will handle the downgrade. - */ - pd->spec_rev = USBPD_REV_30; pd_reset_protocol(pd); if (!pd->in_pr_swap) { @@ -924,7 +937,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) phy_params.data_role = pd->current_dr; phy_params.power_role = pd->current_pr; - phy_params.spec_rev = pd->spec_rev; ret = pd_phy_open(&phy_params); if (ret) { @@ -936,8 +948,6 @@ static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state) } pd->pd_phy_opened = true; - } else { - pd_phy_update_spec_rev(pd->spec_rev); } pd->current_voltage = pd->requested_voltage = 5000000; @@ -1576,7 +1586,6 @@ static void usbpd_sm(struct work_struct *w) memset(&pd->received_pdos, 0, sizeof(pd->received_pdos)); rx_msg_cleanup(pd); - val.intval = 0; power_supply_set_property(pd->usb_psy, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); @@ -1607,6 +1616,10 @@ static void usbpd_sm(struct work_struct *w) usleep_range(ERROR_RECOVERY_TIME * USEC_PER_MSEC, (ERROR_RECOVERY_TIME + 5) * USEC_PER_MSEC); + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); + /* set due to dual_role class "mode" change */ if (pd->forced_pr != POWER_SUPPLY_TYPEC_PR_NONE) val.intval = pd->forced_pr; @@ -1630,11 +1643,22 @@ static void usbpd_sm(struct work_struct *w) if (pd->hard_reset_recvd) { pd->hard_reset_recvd = false; - val.intval = 1; + if (pd->requested_current) { + val.intval = pd->requested_current = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); + } + + pd->requested_voltage = 5000000; + val.intval = pd->requested_voltage; power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val); + POWER_SUPPLY_PROP_VOLTAGE_MIN, &val); pd->in_pr_swap = false; + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); + pd->in_explicit_contract = false; pd->selected_pdo = pd->requested_pdo = 0; pd->rdo = 0; @@ -1895,6 +1919,9 @@ static void usbpd_sm(struct work_struct *w) case PE_SNK_WAIT_FOR_CAPABILITIES: pd->in_pr_swap = false; + val.intval = 0; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) { val.intval = 0; @@ -1914,15 +1941,6 @@ static void usbpd_sm(struct work_struct *w) POWER_SUPPLY_PROP_PD_ACTIVE, &val); } else if (pd->hard_reset_count < 3) { usbpd_set_state(pd, PE_SNK_HARD_RESET); - } else if (pd->pd_connected) { - usbpd_info(&pd->dev, "Sink hard reset count exceeded, forcing reconnect\n"); - - val.intval = 0; - power_supply_set_property(pd->usb_psy, - POWER_SUPPLY_PROP_PD_IN_HARD_RESET, - &val); - - usbpd_set_state(pd, PE_ERROR_RECOVERY); } else { usbpd_dbg(&pd->dev, "Sink hard reset count exceeded, disabling PD\n"); @@ -2073,6 +2091,9 @@ static void usbpd_sm(struct work_struct *w) } pd->in_pr_swap = true; + val.intval = 1; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); break; } else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP)) { @@ -2216,6 +2237,9 @@ static void usbpd_sm(struct work_struct *w) case PE_PRS_SRC_SNK_TRANSITION_TO_OFF: pd->in_pr_swap = true; + val.intval = 1; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); pd->in_explicit_contract = false; if (pd->vbus_enabled) { @@ -2256,6 +2280,9 @@ static void usbpd_sm(struct work_struct *w) } pd->in_pr_swap = true; + val.intval = 1; + power_supply_set_property(pd->usb_psy, + POWER_SUPPLY_PROP_PR_SWAP, &val); usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF); break; @@ -2529,6 +2556,7 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role, { struct usbpd *pd = dual_role_get_drvdata(dual_role); bool do_swap = false; + int wait_count = 5; if (!pd) return -ENODEV; @@ -2555,9 +2583,15 @@ static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role, set_power_role(pd, PR_NONE); /* wait until it takes effect */ - while (pd->forced_pr != POWER_SUPPLY_TYPEC_PR_NONE) + while (pd->forced_pr != POWER_SUPPLY_TYPEC_PR_NONE && + --wait_count) msleep(20); + if (!wait_count) { + usbpd_err(&pd->dev, "setting mode timed out\n"); + return -ETIMEDOUT; + } + break; case DUAL_ROLE_PROP_DR: @@ -3314,6 +3348,8 @@ struct usbpd *usbpd_create(struct device *parent) pd->dual_role->drv_data = pd; } + /* default support as PD 2.0 source or sink */ + pd->spec_rev = USBPD_REV_20; pd->current_pr = PR_NONE; pd->current_dr = DR_NONE; list_add_tail(&pd->instance, &_usbpd); diff --git a/drivers/usb/pd/qpnp-pdphy.c b/drivers/usb/pd/qpnp-pdphy.c index 588af94db6cd..e200c25bc23a 100644 --- a/drivers/usb/pd/qpnp-pdphy.c +++ b/drivers/usb/pd/qpnp-pdphy.c @@ -108,6 +108,7 @@ struct usb_pdphy { int tx_status; u8 frame_filter_val; bool in_test_data_mode; + bool rx_busy; enum data_role data_role; enum power_role power_role; @@ -334,15 +335,6 @@ int pd_phy_update_roles(enum data_role dr, enum power_role pr) } EXPORT_SYMBOL(pd_phy_update_roles); -int pd_phy_update_spec_rev(enum pd_spec_rev rev) -{ - struct usb_pdphy *pdphy = __pdphy; - - return pdphy_masked_write(pdphy, USB_PDPHY_MSG_CONFIG, - MSG_CONFIG_SPEC_REV_MASK, rev); -} -EXPORT_SYMBOL(pd_phy_update_spec_rev); - int pd_phy_open(struct pd_phy_params *params) { int ret; @@ -377,7 +369,9 @@ int pd_phy_open(struct pd_phy_params *params) if (ret) return ret; - ret = pd_phy_update_spec_rev(params->spec_rev); + /* PD 2.0 phy */ + ret = pdphy_masked_write(pdphy, USB_PDPHY_MSG_CONFIG, + MSG_CONFIG_SPEC_REV_MASK, USBPD_REV_20); if (ret) return ret; @@ -492,6 +486,12 @@ int pd_phy_write(u16 hdr, const u8 *data, size_t data_len, return -EINVAL; } + ret = pdphy_reg_read(pdphy, &val, USB_PDPHY_RX_ACKNOWLEDGE, 1); + if (ret || val || pdphy->rx_busy) { + dev_err(pdphy->dev, "%s: RX message pending\n", __func__); + return -EBUSY; + } + pdphy->tx_status = -EINPROGRESS; /* write 2 byte SOP message header */ @@ -664,6 +664,15 @@ static int pd_phy_bist_mode(u8 bist_mode) BIST_MODE_MASK | BIST_ENABLE, bist_mode | BIST_ENABLE); } +static irqreturn_t pdphy_msg_rx_irq(int irq, void *data) +{ + struct usb_pdphy *pdphy = data; + + pdphy->rx_busy = true; + + return IRQ_WAKE_THREAD; +} + static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data) { u8 size, rx_status, frame_type; @@ -720,6 +729,7 @@ static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data) false); pdphy->rx_bytes += size + 1; done: + pdphy->rx_busy = false; return IRQ_HANDLED; } @@ -805,7 +815,7 @@ static int pdphy_probe(struct platform_device *pdev) return ret; ret = pdphy_request_irq(pdphy, pdev->dev.of_node, - &pdphy->msg_rx_irq, "msg-rx", NULL, + &pdphy->msg_rx_irq, "msg-rx", pdphy_msg_rx_irq, pdphy_msg_rx_irq_thread, (IRQF_TRIGGER_RISING | IRQF_ONESHOT)); if (ret < 0) return ret; diff --git a/drivers/usb/pd/usbpd.h b/drivers/usb/pd/usbpd.h index b2663add7f3c..108701739f89 100644 --- a/drivers/usb/pd/usbpd.h +++ b/drivers/usb/pd/usbpd.h @@ -68,7 +68,6 @@ struct pd_phy_params { enum data_role data_role; enum power_role power_role; u8 frame_filter_val; - u8 spec_rev; }; #if IS_ENABLED(CONFIG_QPNP_USB_PDPHY) @@ -77,7 +76,6 @@ int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms); int pd_phy_write(u16 hdr, const u8 *data, size_t data_len, enum pd_msg_type type, unsigned int timeout_ms); int pd_phy_update_roles(enum data_role dr, enum power_role pr); -int pd_phy_update_spec_rev(enum pd_spec_rev rev); void pd_phy_close(void); #else static inline int pd_phy_open(struct pd_phy_params *params) @@ -101,11 +99,6 @@ static inline int pd_phy_update_roles(enum data_role dr, enum power_role pr) return -ENODEV; } -static inline int pd_phy_update_spec_rev(enum pd_spec_rev rev) -{ - return -ENODEV; -} - static inline void pd_phy_close(void) { } diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index b3a21fcbbaf9..e0385d6c0abb 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -809,10 +809,10 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) }, { USB_DEVICE(FTDI_VID, CYBER_CORTEX_AV_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, - { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID), - .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, + { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_PID, 1) }, + { USB_DEVICE_INTERFACE_NUMBER(OLIMEX_VID, OLIMEX_ARM_USB_TINY_H_PID, 1) }, { USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, { USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID), @@ -873,6 +873,7 @@ static const struct usb_device_id id_table_combined[] = { { USB_DEVICE_AND_INTERFACE_INFO(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID, USB_CLASS_VENDOR_SPEC, USB_SUBCLASS_VENDOR_SPEC, 0x00) }, + { USB_DEVICE_INTERFACE_NUMBER(ACTEL_VID, MICROSEMI_ARROW_SF2PLUS_BOARD_PID, 2) }, { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) }, { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID), .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, @@ -1507,9 +1508,9 @@ static int set_serial_info(struct tty_struct *tty, (new_serial.flags & ASYNC_FLAGS)); priv->custom_divisor = new_serial.custom_divisor; +check_and_exit: write_latency_timer(port); -check_and_exit: if ((old_priv.flags & ASYNC_SPD_MASK) != (priv->flags & ASYNC_SPD_MASK)) { if ((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index 48ee04c94a75..4fcf1cecb6d7 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -873,9 +873,17 @@ #define FIC_VID 0x1457 #define FIC_NEO1973_DEBUG_PID 0x5118 +/* + * Actel / Microsemi + */ +#define ACTEL_VID 0x1514 +#define MICROSEMI_ARROW_SF2PLUS_BOARD_PID 0x2008 + /* Olimex */ #define OLIMEX_VID 0x15BA #define OLIMEX_ARM_USB_OCD_PID 0x0003 +#define OLIMEX_ARM_USB_TINY_PID 0x0004 +#define OLIMEX_ARM_USB_TINY_H_PID 0x002a #define OLIMEX_ARM_USB_OCD_H_PID 0x002b /* diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index f1a8fdcd8674..e98532feb0cc 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2349,8 +2349,11 @@ static void change_port_settings(struct tty_struct *tty, if (!baud) { /* pick a default, any default... */ baud = 9600; - } else + } else { + /* Avoid a zero divisor. */ + baud = min(baud, 461550); tty_encode_baud_rate(tty, baud, baud); + } edge_port->baud_rate = baud; config->wBaudRate = (__u16)((461550L + baud/2) / baud); diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 9bf82c262c5b..a6c07c6be25f 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -189,7 +189,7 @@ static int mct_u232_set_baud_rate(struct tty_struct *tty, return -ENOMEM; divisor = mct_u232_calculate_baud_rate(serial, value, &speed); - put_unaligned_le32(cpu_to_le32(divisor), buf); + put_unaligned_le32(divisor, buf); rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), MCT_U232_SET_BAUD_RATE_REQUEST, MCT_U232_SET_REQUEST_TYPE, diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index af67a0de6b5d..3bf61acfc26b 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -281,6 +281,7 @@ static void option_instat_callback(struct urb *urb); #define TELIT_PRODUCT_LE922_USBCFG0 0x1042 #define TELIT_PRODUCT_LE922_USBCFG3 0x1043 #define TELIT_PRODUCT_LE922_USBCFG5 0x1045 +#define TELIT_PRODUCT_ME910 0x1100 #define TELIT_PRODUCT_LE920 0x1200 #define TELIT_PRODUCT_LE910 0x1201 #define TELIT_PRODUCT_LE910_USBCFG4 0x1206 @@ -640,6 +641,11 @@ static const struct option_blacklist_info simcom_sim7100e_blacklist = { .reserved = BIT(5) | BIT(6), }; +static const struct option_blacklist_info telit_me910_blacklist = { + .sendsetup = BIT(0), + .reserved = BIT(1) | BIT(3), +}; + static const struct option_blacklist_info telit_le910_blacklist = { .sendsetup = BIT(0), .reserved = BIT(1) | BIT(2), @@ -1235,6 +1241,8 @@ static const struct usb_device_id option_ids[] = { .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg3 }, { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG5, 0xff), .driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910), + .driver_info = (kernel_ulong_t)&telit_me910_blacklist }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910), .driver_info = (kernel_ulong_t)&telit_le910_blacklist }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4), diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 38b3f0d8cd58..fd509ed6cf70 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -162,6 +162,8 @@ static const struct usb_device_id id_table[] = { {DEVICE_SWI(0x1199, 0x9071)}, /* Sierra Wireless MC74xx */ {DEVICE_SWI(0x1199, 0x9078)}, /* Sierra Wireless EM74xx */ {DEVICE_SWI(0x1199, 0x9079)}, /* Sierra Wireless EM74xx */ + {DEVICE_SWI(0x1199, 0x907a)}, /* Sierra Wireless EM74xx QDL */ + {DEVICE_SWI(0x1199, 0x907b)}, /* Sierra Wireless EM74xx */ {DEVICE_SWI(0x413c, 0x81a2)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */ {DEVICE_SWI(0x413c, 0x81a3)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */ {DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */ diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index f3cf4cecd2b7..091e8ec7a6c0 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -446,6 +446,10 @@ struct ms_lib_ctrl { #define SD_BLOCK_LEN 9 struct ene_ub6250_info { + + /* I/O bounce buffer */ + u8 *bbuf; + /* for 6250 code */ struct SD_STATUS SD_Status; struct MS_STATUS MS_Status; @@ -493,8 +497,11 @@ static int ene_load_bincode(struct us_data *us, unsigned char flag); static void ene_ub6250_info_destructor(void *extra) { + struct ene_ub6250_info *info = (struct ene_ub6250_info *) extra; + if (!extra) return; + kfree(info->bbuf); } static int ene_send_scsi_cmd(struct us_data *us, u8 fDir, void *buf, int use_sg) @@ -858,8 +865,9 @@ static int ms_read_readpage(struct us_data *us, u32 PhyBlockAddr, u8 PageNum, u32 *PageBuf, struct ms_lib_type_extdat *ExtraDat) { struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; int result; - u8 ExtBuf[4]; u32 bn = PhyBlockAddr * 0x20 + PageNum; /* printk(KERN_INFO "MS --- MS_ReaderReadPage, @@ -902,7 +910,7 @@ static int ms_read_readpage(struct us_data *us, u32 PhyBlockAddr, bcb->CDB[2] = (unsigned char)(PhyBlockAddr>>16); bcb->CDB[6] = 0x01; - result = ene_send_scsi_cmd(us, FDIR_READ, &ExtBuf, 0); + result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -911,9 +919,9 @@ static int ms_read_readpage(struct us_data *us, u32 PhyBlockAddr, ExtraDat->status0 = 0x10; /* Not yet,fireware support */ ExtraDat->status1 = 0x00; /* Not yet,fireware support */ - ExtraDat->ovrflg = ExtBuf[0]; - ExtraDat->mngflg = ExtBuf[1]; - ExtraDat->logadr = memstick_logaddr(ExtBuf[2], ExtBuf[3]); + ExtraDat->ovrflg = bbuf[0]; + ExtraDat->mngflg = bbuf[1]; + ExtraDat->logadr = memstick_logaddr(bbuf[2], bbuf[3]); return USB_STOR_TRANSPORT_GOOD; } @@ -1339,8 +1347,9 @@ static int ms_lib_read_extra(struct us_data *us, u32 PhyBlock, u8 PageNum, struct ms_lib_type_extdat *ExtraDat) { struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; + struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; int result; - u8 ExtBuf[4]; /* printk("MS_LibReadExtra --- PhyBlock = %x, PageNum = %x\n", PhyBlock, PageNum); */ memset(bcb, 0, sizeof(struct bulk_cb_wrap)); @@ -1355,7 +1364,7 @@ static int ms_lib_read_extra(struct us_data *us, u32 PhyBlock, bcb->CDB[2] = (unsigned char)(PhyBlock>>16); bcb->CDB[6] = 0x01; - result = ene_send_scsi_cmd(us, FDIR_READ, &ExtBuf, 0); + result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -1363,9 +1372,9 @@ static int ms_lib_read_extra(struct us_data *us, u32 PhyBlock, ExtraDat->intr = 0x80; /* Not yet, waiting for fireware support */ ExtraDat->status0 = 0x10; /* Not yet, waiting for fireware support */ ExtraDat->status1 = 0x00; /* Not yet, waiting for fireware support */ - ExtraDat->ovrflg = ExtBuf[0]; - ExtraDat->mngflg = ExtBuf[1]; - ExtraDat->logadr = memstick_logaddr(ExtBuf[2], ExtBuf[3]); + ExtraDat->ovrflg = bbuf[0]; + ExtraDat->mngflg = bbuf[1]; + ExtraDat->logadr = memstick_logaddr(bbuf[2], bbuf[3]); return USB_STOR_TRANSPORT_GOOD; } @@ -1569,9 +1578,9 @@ static int ms_lib_scan_logicalblocknumber(struct us_data *us, u16 btBlk1st) u16 PhyBlock, newblk, i; u16 LogStart, LogEnde; struct ms_lib_type_extdat extdat; - u8 buf[0x200]; u32 count = 0, index = 0; struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; for (PhyBlock = 0; PhyBlock < info->MS_Lib.NumberOfPhyBlock;) { ms_lib_phy_to_log_range(PhyBlock, &LogStart, &LogEnde); @@ -1585,14 +1594,16 @@ static int ms_lib_scan_logicalblocknumber(struct us_data *us, u16 btBlk1st) } if (count == PhyBlock) { - ms_lib_read_extrablock(us, PhyBlock, 0, 0x80, &buf); + ms_lib_read_extrablock(us, PhyBlock, 0, 0x80, + bbuf); count += 0x80; } index = (PhyBlock % 0x80) * 4; - extdat.ovrflg = buf[index]; - extdat.mngflg = buf[index+1]; - extdat.logadr = memstick_logaddr(buf[index+2], buf[index+3]); + extdat.ovrflg = bbuf[index]; + extdat.mngflg = bbuf[index+1]; + extdat.logadr = memstick_logaddr(bbuf[index+2], + bbuf[index+3]); if ((extdat.ovrflg & MS_REG_OVR_BKST) != MS_REG_OVR_BKST_OK) { ms_lib_setacquired_errorblock(us, PhyBlock); @@ -2075,9 +2086,9 @@ static int ene_ms_init(struct us_data *us) { struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; int result; - u8 buf[0x200]; u16 MSP_BlockSize, MSP_UserAreaBlocks; struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; printk(KERN_INFO "transport --- ENE_MSInit\n"); @@ -2096,13 +2107,13 @@ static int ene_ms_init(struct us_data *us) bcb->CDB[0] = 0xF1; bcb->CDB[1] = 0x01; - result = ene_send_scsi_cmd(us, FDIR_READ, &buf, 0); + result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0); if (result != USB_STOR_XFER_GOOD) { printk(KERN_ERR "Execution MS Init Code Fail !!\n"); return USB_STOR_TRANSPORT_ERROR; } /* the same part to test ENE */ - info->MS_Status = *(struct MS_STATUS *)&buf[0]; + info->MS_Status = *(struct MS_STATUS *) bbuf; if (info->MS_Status.Insert && info->MS_Status.Ready) { printk(KERN_INFO "Insert = %x\n", info->MS_Status.Insert); @@ -2111,15 +2122,15 @@ static int ene_ms_init(struct us_data *us) printk(KERN_INFO "IsMSPHG = %x\n", info->MS_Status.IsMSPHG); printk(KERN_INFO "WtP= %x\n", info->MS_Status.WtP); if (info->MS_Status.IsMSPro) { - MSP_BlockSize = (buf[6] << 8) | buf[7]; - MSP_UserAreaBlocks = (buf[10] << 8) | buf[11]; + MSP_BlockSize = (bbuf[6] << 8) | bbuf[7]; + MSP_UserAreaBlocks = (bbuf[10] << 8) | bbuf[11]; info->MSP_TotalBlock = MSP_BlockSize * MSP_UserAreaBlocks; } else { ms_card_init(us); /* Card is MS (to ms.c)*/ } usb_stor_dbg(us, "MS Init Code OK !!\n"); } else { - usb_stor_dbg(us, "MS Card Not Ready --- %x\n", buf[0]); + usb_stor_dbg(us, "MS Card Not Ready --- %x\n", bbuf[0]); return USB_STOR_TRANSPORT_ERROR; } @@ -2129,9 +2140,9 @@ static int ene_ms_init(struct us_data *us) static int ene_sd_init(struct us_data *us) { int result; - u8 buf[0x200]; struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; struct ene_ub6250_info *info = (struct ene_ub6250_info *) us->extra; + u8 *bbuf = info->bbuf; usb_stor_dbg(us, "transport --- ENE_SDInit\n"); /* SD Init Part-1 */ @@ -2165,17 +2176,17 @@ static int ene_sd_init(struct us_data *us) bcb->Flags = US_BULK_FLAG_IN; bcb->CDB[0] = 0xF1; - result = ene_send_scsi_cmd(us, FDIR_READ, &buf, 0); + result = ene_send_scsi_cmd(us, FDIR_READ, bbuf, 0); if (result != USB_STOR_XFER_GOOD) { usb_stor_dbg(us, "Execution SD Init Code Fail !!\n"); return USB_STOR_TRANSPORT_ERROR; } - info->SD_Status = *(struct SD_STATUS *)&buf[0]; + info->SD_Status = *(struct SD_STATUS *) bbuf; if (info->SD_Status.Insert && info->SD_Status.Ready) { struct SD_STATUS *s = &info->SD_Status; - ene_get_card_status(us, (unsigned char *)&buf); + ene_get_card_status(us, bbuf); usb_stor_dbg(us, "Insert = %x\n", s->Insert); usb_stor_dbg(us, "Ready = %x\n", s->Ready); usb_stor_dbg(us, "IsMMC = %x\n", s->IsMMC); @@ -2183,7 +2194,7 @@ static int ene_sd_init(struct us_data *us) usb_stor_dbg(us, "HiSpeed = %x\n", s->HiSpeed); usb_stor_dbg(us, "WtP = %x\n", s->WtP); } else { - usb_stor_dbg(us, "SD Card Not Ready --- %x\n", buf[0]); + usb_stor_dbg(us, "SD Card Not Ready --- %x\n", bbuf[0]); return USB_STOR_TRANSPORT_ERROR; } return USB_STOR_TRANSPORT_GOOD; @@ -2193,13 +2204,15 @@ static int ene_sd_init(struct us_data *us) static int ene_init(struct us_data *us) { int result; - u8 misc_reg03 = 0; + u8 misc_reg03; struct ene_ub6250_info *info = (struct ene_ub6250_info *)(us->extra); + u8 *bbuf = info->bbuf; - result = ene_get_card_type(us, REG_CARD_STATUS, &misc_reg03); + result = ene_get_card_type(us, REG_CARD_STATUS, bbuf); if (result != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; + misc_reg03 = bbuf[0]; if (misc_reg03 & 0x01) { if (!info->SD_Status.Ready) { result = ene_sd_init(us); @@ -2316,8 +2329,9 @@ static int ene_ub6250_probe(struct usb_interface *intf, const struct usb_device_id *id) { int result; - u8 misc_reg03 = 0; + u8 misc_reg03; struct us_data *us; + struct ene_ub6250_info *info; result = usb_stor_probe1(&us, intf, id, (id - ene_ub6250_usb_ids) + ene_ub6250_unusual_dev_list, @@ -2326,11 +2340,16 @@ static int ene_ub6250_probe(struct usb_interface *intf, return result; /* FIXME: where should the code alloc extra buf ? */ - if (!us->extra) { - us->extra = kzalloc(sizeof(struct ene_ub6250_info), GFP_KERNEL); - if (!us->extra) - return -ENOMEM; - us->extra_destructor = ene_ub6250_info_destructor; + us->extra = kzalloc(sizeof(struct ene_ub6250_info), GFP_KERNEL); + if (!us->extra) + return -ENOMEM; + us->extra_destructor = ene_ub6250_info_destructor; + + info = (struct ene_ub6250_info *)(us->extra); + info->bbuf = kmalloc(512, GFP_KERNEL); + if (!info->bbuf) { + kfree(us->extra); + return -ENOMEM; } us->transport_name = "ene_ub6250"; @@ -2342,12 +2361,13 @@ static int ene_ub6250_probe(struct usb_interface *intf, return result; /* probe card type */ - result = ene_get_card_type(us, REG_CARD_STATUS, &misc_reg03); + result = ene_get_card_type(us, REG_CARD_STATUS, info->bbuf); if (result != USB_STOR_XFER_GOOD) { usb_stor_disconnect(intf); return USB_STOR_TRANSPORT_ERROR; } + misc_reg03 = info->bbuf[0]; if (!(misc_reg03 & 0x01)) { pr_info("ums_eneub6250: This driver only supports SD/MS cards. " "It does not support SM cards.\n"); diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c index 6345e85822a4..a50cf45e530f 100644 --- a/drivers/uwb/i1480/dfu/usb.c +++ b/drivers/uwb/i1480/dfu/usb.c @@ -341,6 +341,7 @@ error_submit_ep1: static int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id) { + struct usb_device *udev = interface_to_usbdev(iface); struct i1480_usb *i1480_usb; struct i1480 *i1480; struct device *dev = &iface->dev; @@ -352,8 +353,8 @@ int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id) iface->cur_altsetting->desc.bInterfaceNumber); goto error; } - if (iface->num_altsetting > 1 - && interface_to_usbdev(iface)->descriptor.idProduct == 0xbabe) { + if (iface->num_altsetting > 1 && + le16_to_cpu(udev->descriptor.idProduct) == 0xbabe) { /* Need altsetting #1 [HW QUIRK] or EP1 won't work */ result = usb_set_interface(interface_to_usbdev(iface), 0, 1); if (result < 0) diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c index ecb826eefe02..2fa280671c1e 100644 --- a/drivers/vfio/vfio_iommu_type1.c +++ b/drivers/vfio/vfio_iommu_type1.c @@ -130,57 +130,34 @@ static void vfio_unlink_dma(struct vfio_iommu *iommu, struct vfio_dma *old) rb_erase(&old->node, &iommu->dma_list); } -struct vwork { - struct mm_struct *mm; - long npage; - struct work_struct work; -}; - -/* delayed decrement/increment for locked_vm */ -static void vfio_lock_acct_bg(struct work_struct *work) +static int vfio_lock_acct(long npage, bool *lock_cap) { - struct vwork *vwork = container_of(work, struct vwork, work); - struct mm_struct *mm; - - mm = vwork->mm; - down_write(&mm->mmap_sem); - mm->locked_vm += vwork->npage; - up_write(&mm->mmap_sem); - mmput(mm); - kfree(vwork); -} + int ret = 0; -static void vfio_lock_acct(long npage) -{ - struct vwork *vwork; - struct mm_struct *mm; + if (!npage) + return 0; - if (!current->mm || !npage) - return; /* process exited or nothing to do */ + if (!current->mm) + return -ESRCH; /* process exited */ - if (down_write_trylock(¤t->mm->mmap_sem)) { - current->mm->locked_vm += npage; - up_write(¤t->mm->mmap_sem); - return; - } + down_write(¤t->mm->mmap_sem); + if (npage > 0) { + if (lock_cap ? !*lock_cap : !capable(CAP_IPC_LOCK)) { + unsigned long limit; - /* - * Couldn't get mmap_sem lock, so must setup to update - * mm->locked_vm later. If locked_vm were atomic, we - * wouldn't need this silliness - */ - vwork = kmalloc(sizeof(struct vwork), GFP_KERNEL); - if (!vwork) - return; - mm = get_task_mm(current); - if (!mm) { - kfree(vwork); - return; + limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + + if (current->mm->locked_vm + npage > limit) + ret = -ENOMEM; + } } - INIT_WORK(&vwork->work, vfio_lock_acct_bg); - vwork->mm = mm; - vwork->npage = npage; - schedule_work(&vwork->work); + + if (!ret) + current->mm->locked_vm += npage; + + up_write(¤t->mm->mmap_sem); + + return ret; } /* @@ -262,9 +239,9 @@ static int vaddr_get_pfn(unsigned long vaddr, int prot, unsigned long *pfn) static long vfio_pin_pages(unsigned long vaddr, long npage, int prot, unsigned long *pfn_base) { - unsigned long limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + unsigned long pfn = 0, limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; bool lock_cap = capable(CAP_IPC_LOCK); - long ret, i; + long ret, i = 1; bool rsvd; if (!current->mm) @@ -283,16 +260,11 @@ static long vfio_pin_pages(unsigned long vaddr, long npage, return -ENOMEM; } - if (unlikely(disable_hugepages)) { - if (!rsvd) - vfio_lock_acct(1); - return 1; - } + if (unlikely(disable_hugepages)) + goto out; /* Lock all the consecutive pages from pfn_base */ - for (i = 1, vaddr += PAGE_SIZE; i < npage; i++, vaddr += PAGE_SIZE) { - unsigned long pfn = 0; - + for (vaddr += PAGE_SIZE; i < npage; i++, vaddr += PAGE_SIZE) { ret = vaddr_get_pfn(vaddr, prot, &pfn); if (ret) break; @@ -308,12 +280,24 @@ static long vfio_pin_pages(unsigned long vaddr, long npage, put_pfn(pfn, prot); pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n", __func__, limit << PAGE_SHIFT); - break; + ret = -ENOMEM; + goto unpin_out; } } +out: if (!rsvd) - vfio_lock_acct(i); + ret = vfio_lock_acct(i, &lock_cap); + +unpin_out: + if (ret) { + if (!rsvd) { + for (pfn = *pfn_base ; i ; pfn++, i--) + put_pfn(pfn, prot); + } + + return ret; + } return i; } @@ -328,7 +312,7 @@ static long vfio_unpin_pages(unsigned long pfn, long npage, unlocked += put_pfn(pfn++, prot); if (do_accounting) - vfio_lock_acct(-unlocked); + vfio_lock_acct(-unlocked, NULL); return unlocked; } @@ -390,7 +374,7 @@ static void vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma) cond_resched(); } - vfio_lock_acct(-unlocked); + vfio_lock_acct(-unlocked, NULL); } static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *dma) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 7d32e2c5cc0d..7ab2fb13061d 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -25,6 +25,7 @@ source "drivers/gpu/msm/Kconfig" source "drivers/gpu/drm/Kconfig" +source "drivers/video/msm/ba/Kconfig" menu "Frame buffer Devices" source "drivers/video/fbdev/Kconfig" endmenu diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 1a8c4ced39b2..0a190665a4cf 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -1,3 +1,4 @@ +obj-$(CONFIG_MSM_BA_V4L2) += msm/ba/ obj-$(CONFIG_VGASTATE) += vgastate.o obj-$(CONFIG_HDMI) += hdmi.o diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index c082ae8e50ce..2a708dd2ecee 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -1097,6 +1097,13 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, void __user *argp = (void __user *)arg; long ret = 0; + memset(&var, 0, sizeof(var)); + memset(&fix, 0, sizeof(fix)); + memset(&con2fb, 0, sizeof(con2fb)); + memset(&cmap_from, 0, sizeof(cmap_from)); + memset(&cmap, 0, sizeof(cmap)); + memset(&event, 0, sizeof(event)); + switch (cmd) { case FBIOGET_VSCREENINFO: if (!lock_fb_info(info)) diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index 1c984d02755e..d6e8a213c215 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -272,7 +272,7 @@ struct mdss_smmu_ops { void (*smmu_unmap_dma_buf)(struct sg_table *table, int domain, int dir, struct dma_buf *dma_buf); int (*smmu_dma_alloc_coherent)(struct device *dev, size_t size, - dma_addr_t *phys, dma_addr_t *iova, void *cpu_addr, + dma_addr_t *phys, dma_addr_t *iova, void **cpu_addr, gfp_t gfp, int domain); void (*smmu_dma_free_coherent)(struct device *dev, size_t size, void *cpu_addr, dma_addr_t phys, dma_addr_t iova, diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c index 2b9c71441d68..2f5aad8ed801 100644 --- a/drivers/video/fbdev/msm/mdss_compat_utils.c +++ b/drivers/video/fbdev/msm/mdss_compat_utils.c @@ -3493,6 +3493,7 @@ static int __copy_layer_pp_info_igc_params( compat_ptr(pp_info32->igc_cfg.c0_c1_data); pp_info->igc_cfg.c2_data = compat_ptr(pp_info32->igc_cfg.c2_data); + kfree(cfg_payload); cfg_payload = NULL; break; } @@ -3565,6 +3566,7 @@ static int __copy_layer_pp_info_hist_lut_params( pp_info->hist_lut_cfg.len = pp_info32->hist_lut_cfg.len; pp_info->hist_lut_cfg.data = compat_ptr(pp_info32->hist_lut_cfg.data); + kfree(cfg_payload); cfg_payload = NULL; break; } @@ -3654,6 +3656,7 @@ static int __copy_layer_pp_info_pa_v2_params( break; default: pr_debug("version invalid\n"); + kfree(cfg_payload); cfg_payload = NULL; break; } @@ -3737,6 +3740,7 @@ static int __copy_layer_pp_info_pcc_params( break; default: pr_debug("version invalid, fallback to legacy\n"); + kfree(cfg_payload); cfg_payload = NULL; break; } diff --git a/drivers/video/fbdev/msm/mdss_dba_utils.c b/drivers/video/fbdev/msm/mdss_dba_utils.c index 3330f8f62b78..c6ff92ed1686 100644 --- a/drivers/video/fbdev/msm/mdss_dba_utils.c +++ b/drivers/video/fbdev/msm/mdss_dba_utils.c @@ -576,7 +576,6 @@ int mdss_dba_utils_video_on(void *data, struct mdss_panel_info *pinfo) video_cfg.h_pulse_width = pinfo->lcdc.h_pulse_width; video_cfg.v_pulse_width = pinfo->lcdc.v_pulse_width; video_cfg.pclk_khz = (unsigned long)pinfo->clk_rate / 1000; - video_cfg.hdmi_mode = !hdmi_edid_is_dvi_mode(ud->edid_data); /* Calculate number of DSI lanes configured */ video_cfg.num_of_input_lanes = 0; @@ -592,6 +591,8 @@ int mdss_dba_utils_video_on(void *data, struct mdss_panel_info *pinfo) /* Get scan information from EDID */ video_cfg.vic = mdss_dba_get_vic_panel_info(ud, pinfo); ud->current_vic = video_cfg.vic; + video_cfg.hdmi_mode = hdmi_edid_get_sink_mode(ud->edid_data, + video_cfg.vic); video_cfg.scaninfo = hdmi_edid_get_sink_scaninfo(ud->edid_data, video_cfg.vic); if (ud->ops.video_on) diff --git a/drivers/video/fbdev/msm/mdss_debug.c b/drivers/video/fbdev/msm/mdss_debug.c index 8cb6c7157230..230b02061b39 100644 --- a/drivers/video/fbdev/msm/mdss_debug.c +++ b/drivers/video/fbdev/msm/mdss_debug.c @@ -454,6 +454,9 @@ static ssize_t mdss_debug_base_offset_write(struct file *file, sscanf(buf, "%5x %x", &off, &cnt); + if (off % sizeof(u32)) + return -EINVAL; + if (off > dbg->max_offset) return -EINVAL; @@ -526,6 +529,9 @@ static ssize_t mdss_debug_base_reg_write(struct file *file, if (cnt < 2) return -EFAULT; + if (off % sizeof(u32)) + return -EFAULT; + if (off >= dbg->max_offset) return -EFAULT; @@ -571,6 +577,9 @@ static ssize_t mdss_debug_base_reg_read(struct file *file, return -ENOMEM; } + if (dbg->off % sizeof(u32)) + return -EFAULT; + ptr = dbg->base + dbg->off; tot = 0; diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index a74bf6f60774..c8afb3a244c3 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -1455,7 +1455,7 @@ static void mdss_dp_configure_source_params(struct mdss_dp_drv_pdata *dp, mdss_dp_config_misc(dp, mdss_dp_bpp_to_test_bit_depth(mdss_dp_get_bpp(dp)), mdss_dp_get_colorimetry_config(dp)); - mdss_dp_sw_config_msa(&dp->ctrl_io, dp->link_rate, &dp->dp_cc_io); + mdss_dp_sw_config_msa(dp); mdss_dp_timing_cfg(&dp->ctrl_io, &dp->panel_data.panel_info); } @@ -1674,6 +1674,8 @@ exit: int mdss_dp_on(struct mdss_panel_data *pdata) { struct mdss_dp_drv_pdata *dp_drv = NULL; + bool hpd; + int rc = 0; if (!pdata) { pr_err("Invalid input data\n"); @@ -1683,6 +1685,22 @@ int mdss_dp_on(struct mdss_panel_data *pdata) dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); + mutex_lock(&dp_drv->attention_lock); + hpd = dp_drv->cable_connected; + mutex_unlock(&dp_drv->attention_lock); + + /* In case of device coming out of PM_SUSPEND, there can be + * a corner case where the sink is turned off or the DP cable + * is disconnected almost at the same time as userspace triggering + * unblank. This can cause the UNBLANK call to be still triggered + * before the disconnect event is notified to the userspace. + * Avoid turning ON DP path in such cases. + */ + if (!hpd || !dp_drv->alt_mode.dp_status.hpd_high) { + pr_err("DP sink not connected\n"); + return -EINVAL; + } + /* * If the link already active, then nothing needs to be done here. * However, it is possible that the the power_on flag could be @@ -1707,15 +1725,18 @@ int mdss_dp_on(struct mdss_panel_data *pdata) * init/deinit during unrelated resume/suspend events, * add host initialization call before DP power-on. */ - if (!dp_drv->dp_initialized) - mdss_dp_host_init(pdata); + if (!dp_drv->dp_initialized) { + rc = mdss_dp_host_init(pdata); + if (rc < 0) + return rc; + } return mdss_dp_on_hpd(dp_drv); } static bool mdss_dp_is_ds_bridge(struct mdss_dp_drv_pdata *dp) { - return dp->dpcd.downstream_port.dfp_present; + return dp->dpcd.downstream_port.dsp_present; } static bool mdss_dp_is_ds_bridge_sink_count_zero(struct mdss_dp_drv_pdata *dp) @@ -1941,6 +1962,11 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata) } dp_drv->orientation = usbpd_get_plug_orientation(dp_drv->pd); + if (dp_drv->orientation == ORIENTATION_NONE) { + pr_err("DP cable might be disconnected\n"); + ret = -EINVAL; + goto orientation_error; + } dp_drv->aux_sel_gpio_output = 0; if (dp_drv->orientation == ORIENTATION_CC2) @@ -1981,8 +2007,9 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata) return 0; clk_error: - mdss_dp_regulator_ctrl(dp_drv, false); mdss_dp_config_gpios(dp_drv, false); +orientation_error: + mdss_dp_regulator_ctrl(dp_drv, false); vreg_error: return ret; } diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index f358aad8a667..afa8e3db590f 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -230,14 +230,38 @@ struct dp_alt_mode { #define DP_LINK_RATE_MULTIPLIER 27000000 #define DP_KHZ_TO_HZ 1000 #define DP_MAX_PIXEL_CLK_KHZ 675000 + +enum downstream_port_type { + DSP_TYPE_DP = 0x00, + DSP_TYPE_VGA, + DSP_TYPE_DVI_HDMI_DPPP, + DSP_TYPE_OTHER, +}; + +static inline char *mdss_dp_dsp_type_to_string(u32 dsp_type) +{ + switch (dsp_type) { + case DSP_TYPE_DP: + return DP_ENUM_STR(DSP_TYPE_DP); + case DSP_TYPE_VGA: + return DP_ENUM_STR(DSP_TYPE_VGA); + case DSP_TYPE_DVI_HDMI_DPPP: + return DP_ENUM_STR(DSP_TYPE_DVI_HDMI_DPPP); + case DSP_TYPE_OTHER: + return DP_ENUM_STR(DSP_TYPE_OTHER); + default: + return "unknown"; + } +} + struct downstream_port_config { /* Byte 02205h */ - bool dfp_present; - u32 dfp_type; + bool dsp_present; + enum downstream_port_type dsp_type; bool format_conversion; bool detailed_cap_info_available; /* Byte 02207h */ - u32 dfp_count; + u32 dsp_count; bool msa_timing_par_ignored; bool oui_support; }; @@ -1139,6 +1163,12 @@ static inline void mdss_dp_reset_frame_crc_data(struct mdss_dp_crc_data *crc) crc->en = false; } +static inline bool mdss_dp_is_dsp_type_vga(struct mdss_dp_drv_pdata *dp) +{ + return (dp->dpcd.downstream_port.dsp_present && + (dp->dpcd.downstream_port.dsp_type == DSP_TYPE_VGA)); +} + void mdss_dp_phy_initialize(struct mdss_dp_drv_pdata *dp); int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *dp); diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 37209c161366..23a63121b78c 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -633,7 +633,8 @@ void dp_extract_edid_video_support(struct edp_edid *edid, char *buf) pr_debug("Digital Video intf=%d color_depth=%d\n", edid->video_intf, edid->color_depth); } else { - pr_err("Error, Analog video interface\n"); + pr_debug("Analog video interface, set color depth to 8\n"); + edid->color_depth = DP_TEST_BIT_DEPTH_8; } }; @@ -1140,13 +1141,13 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) pr_debug("rx_ports=%d", cap->num_rx_port); data = *bp++; /* Byte 5: DOWN_STREAM_PORT_PRESENT */ - cap->downstream_port.dfp_present = data & BIT(0); - cap->downstream_port.dfp_type = data & 0x6; + cap->downstream_port.dsp_present = data & BIT(0); + cap->downstream_port.dsp_type = (data & 0x6) >> 1; cap->downstream_port.format_conversion = data & BIT(3); cap->downstream_port.detailed_cap_info_available = data & BIT(4); - pr_debug("dfp_present = %d, dfp_type = %d\n", - cap->downstream_port.dfp_present, - cap->downstream_port.dfp_type); + pr_debug("dsp_present = %d, dsp_type = %d\n", + cap->downstream_port.dsp_present, + cap->downstream_port.dsp_type); pr_debug("format_conversion = %d, detailed_cap_info_available = %d\n", cap->downstream_port.format_conversion, cap->downstream_port.detailed_cap_info_available); @@ -1154,16 +1155,16 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) bp += 1; /* Skip Byte 6 */ data = *bp++; /* Byte 7: DOWN_STREAM_PORT_COUNT */ - cap->downstream_port.dfp_count = data & 0x7; - if (cap->downstream_port.dfp_count > DP_MAX_DS_PORT_COUNT) { + cap->downstream_port.dsp_count = data & 0x7; + if (cap->downstream_port.dsp_count > DP_MAX_DS_PORT_COUNT) { pr_debug("DS port count %d greater that max (%d) supported\n", - cap->downstream_port.dfp_count, DP_MAX_DS_PORT_COUNT); - cap->downstream_port.dfp_count = DP_MAX_DS_PORT_COUNT; + cap->downstream_port.dsp_count, DP_MAX_DS_PORT_COUNT); + cap->downstream_port.dsp_count = DP_MAX_DS_PORT_COUNT; } cap->downstream_port.msa_timing_par_ignored = data & BIT(6); cap->downstream_port.oui_support = data & BIT(7); - pr_debug("dfp_count = %d, msa_timing_par_ignored = %d\n", - cap->downstream_port.dfp_count, + pr_debug("dsp_count = %d, msa_timing_par_ignored = %d\n", + cap->downstream_port.dsp_count, cap->downstream_port.msa_timing_par_ignored); pr_debug("oui_support = %d\n", cap->downstream_port.oui_support); diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c index 0d9cf7b72b4d..f7b3d4664e86 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.c +++ b/drivers/video/fbdev/msm/mdss_dp_util.c @@ -311,7 +311,7 @@ static void mdss_dp_calc_tu_parameters(u8 link_rate, u8 ln_cnt, bool n_err_neg, nn_err_neg; u8 hblank_margin = 16; - u8 tu_size, tu_size_desired, tu_size_minus1; + u8 tu_size, tu_size_desired = 0, tu_size_minus1; int valid_boundary_link; u64 resulting_valid; u64 total_valid; @@ -786,19 +786,56 @@ void mdss_dp_timing_cfg(struct dss_io_data *ctrl_io, writel_relaxed(data, ctrl_io->base + DP_ACTIVE_HOR_VER); } -void mdss_dp_sw_config_msa(struct dss_io_data *ctrl_io, - char lrate, struct dss_io_data *dp_cc_io) +static bool use_fixed_nvid(struct mdss_dp_drv_pdata *dp) +{ + /* + * For better interop experience, used a fixed NVID=0x8000 + * whenever connected to a VGA dongle downstream + */ + return mdss_dp_is_dsp_type_vga(dp); +} + +void mdss_dp_sw_config_msa(struct mdss_dp_drv_pdata *dp) { u32 pixel_m, pixel_n; u32 mvid, nvid; - - pixel_m = readl_relaxed(dp_cc_io->base + MMSS_DP_PIXEL_M); - pixel_n = readl_relaxed(dp_cc_io->base + MMSS_DP_PIXEL_N); - pr_debug("pixel_m=0x%x, pixel_n=0x%x\n", - pixel_m, pixel_n); - - mvid = (pixel_m & 0xFFFF) * 5; - nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF); + u64 mvid_calc; + u32 const nvid_fixed = 0x8000; + struct dss_io_data *ctrl_io = &dp->ctrl_io; + struct dss_io_data *dp_cc_io = &dp->dp_cc_io; + u32 lrate_kbps; + u64 stream_rate_khz; + + if (use_fixed_nvid(dp)) { + pr_debug("use fixed NVID=0x%x\n", nvid_fixed); + nvid = nvid_fixed; + + lrate_kbps = dp->link_rate * DP_LINK_RATE_MULTIPLIER / + DP_KHZ_TO_HZ; + stream_rate_khz = div_u64(dp->panel_data.panel_info.clk_rate, + DP_KHZ_TO_HZ); + pr_debug("link rate=%dkbps, stream_rate_khz=%lluKhz", + lrate_kbps, stream_rate_khz); + + /* + * For intermediate results, use 64 bit arithmetic to avoid + * loss of precision. + */ + mvid_calc = stream_rate_khz * nvid; + mvid_calc = div_u64(mvid_calc, lrate_kbps); + + /* + * truncate back to 32 bits as this final divided value will + * always be within the range of a 32 bit unsigned int. + */ + mvid = (u32) mvid_calc; + } else { + pixel_m = readl_relaxed(dp_cc_io->base + MMSS_DP_PIXEL_M); + pixel_n = readl_relaxed(dp_cc_io->base + MMSS_DP_PIXEL_N); + pr_debug("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n); + mvid = (pixel_m & 0xFFFF) * 5; + nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF); + } pr_debug("mvid=0x%x, nvid=0x%x\n", mvid, nvid); writel_relaxed(mvid, ctrl_io->base + DP_SOFTWARE_MVID); diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h index 4c93e48e97dc..4970f5bc3a47 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.h +++ b/drivers/video/fbdev/msm/mdss_dp_util.h @@ -316,8 +316,7 @@ void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data); int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv); void mdss_dp_irq_enable(struct mdss_dp_drv_pdata *dp_drv); void mdss_dp_irq_disable(struct mdss_dp_drv_pdata *dp_drv); -void mdss_dp_sw_config_msa(struct dss_io_data *ctrl_io, - char lrate, struct dss_io_data *dp_cc_io); +void mdss_dp_sw_config_msa(struct mdss_dp_drv_pdata *dp); void mdss_dp_usbpd_ext_capabilities(struct usbpd_dp_capabilities *dp_cap); void mdss_dp_usbpd_ext_dp_status(struct usbpd_dp_status *dp_status); u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp); diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index d5caf0e6bb1c..82f6d4a123b5 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -773,6 +773,11 @@ static ssize_t mdss_dsi_cmd_state_write(struct file *file, int *link_state = file->private_data; char *input; + if (!count) { + pr_err("%s: Zero bytes to be written\n", __func__); + return -EINVAL; + } + input = kmalloc(count, GFP_KERNEL); if (!input) { pr_err("%s: Failed to allocate memory\n", __func__); @@ -2091,7 +2096,7 @@ static int __mdss_dsi_dfps_update_clks(struct mdss_panel_data *pdata, { struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; struct mdss_dsi_ctrl_pdata *sctrl_pdata = NULL; - struct mdss_panel_info *pinfo, *spinfo; + struct mdss_panel_info *pinfo, *spinfo = NULL; int rc = 0; if (pdata == NULL) { @@ -3429,9 +3434,10 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev) hw_vsync_handler, IRQF_TRIGGER_FALLING, "VSYNC_GPIO", ctrl_pdata); if (rc) { - pr_err("TE request_irq failed.\n"); + pr_err("%s: TE request_irq failed for ESD\n", __func__); goto error_shadow_clk_deinit; } + te_irq_registered = 1; disable_irq(gpio_to_irq(ctrl_pdata->disp_te_gpio)); } @@ -4301,6 +4307,15 @@ static int mdss_dsi_parse_gpio_params(struct platform_device *ctrl_pdev, of_property_read_bool(ctrl_pdev->dev.of_node, "qcom,platform-bklight-en-gpio-invert"); + ctrl_pdata->avdd_en_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, + "qcom,platform-avdd-en-gpio", 0); + if (!gpio_is_valid(ctrl_pdata->avdd_en_gpio)) + pr_info("%s: avdd_en gpio not specified\n", __func__); + + ctrl_pdata->avdd_en_gpio_invert = + of_property_read_bool(ctrl_pdev->dev.of_node, + "qcom,platform-avdd-en-gpio-invert"); + ctrl_pdata->rst_gpio = of_get_named_gpio(ctrl_pdev->dev.of_node, "qcom,platform-reset-gpio", 0); if (!gpio_is_valid(ctrl_pdata->rst_gpio)) diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index 62d88f0af652..9847016fed29 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -455,8 +455,11 @@ struct mdss_dsi_ctrl_pdata { int bklt_en_gpio; bool bklt_en_gpio_invert; bool bklt_en_gpio_state; + int avdd_en_gpio; + bool avdd_en_gpio_invert; int lcd_mode_sel_gpio; int bklt_ctrl; /* backlight ctrl */ + enum dsi_ctrl_op_mode bklt_dcs_op_mode; /* backlight dcs ctrl mode */ bool pwm_pmi; int pwm_period; int pwm_pmic_gpio; diff --git a/drivers/video/fbdev/msm/mdss_dsi_host.c b/drivers/video/fbdev/msm/mdss_dsi_host.c index f0fb791a7b8d..c766ff983045 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_host.c +++ b/drivers/video/fbdev/msm/mdss_dsi_host.c @@ -1161,6 +1161,8 @@ static int mdss_dsi_read_status(struct mdss_dsi_ctrl_pdata *ctrl) rc = mdss_dsi_cmdlist_put(ctrl, &cmdreq); if (rc <= 0) { + if (!mdss_dsi_sync_wait_enable(ctrl) || + mdss_dsi_sync_wait_trigger(ctrl)) pr_err("%s: get status: fail\n", __func__); return rc; } @@ -2273,6 +2275,7 @@ static int mdss_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl, bool ack_error = false; char reg[16] = {0x0}; int repeated_bytes = 0; + struct mdss_dsi_ctrl_pdata *mctrl = mdss_dsi_get_other_ctrl(ctrl); lp = (u32 *)rp->data; temp = (u32 *)reg; @@ -2333,7 +2336,11 @@ static int mdss_dsi_cmd_dma_rx(struct mdss_dsi_ctrl_pdata *ctrl, off += ((cnt - 1) * 4); for (i = 0; i < cnt; i++) { - data = (u32)MIPI_INP((ctrl->ctrl_base) + off); + if (mdss_dsi_sync_wait_trigger(ctrl)) + data = (u32)MIPI_INP((mctrl->ctrl_base) + off); + else + data = (u32)MIPI_INP((ctrl->ctrl_base) + off); + /* to network byte order */ if (!repeated_bytes) *lp++ = ntohl(data); diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index 60012c71449c..dbd58f93e907 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -238,6 +238,11 @@ static void mdss_dsi_panel_bklt_dcs(struct mdss_dsi_ctrl_pdata *ctrl, int level) cmdreq.rlen = 0; cmdreq.cb = NULL; + if (ctrl->bklt_dcs_op_mode == DSI_HS_MODE) + cmdreq.flags |= CMD_REQ_HS_MODE; + else + cmdreq.flags |= CMD_REQ_LP_MODE; + mdss_dsi_cmdlist_put(ctrl, &cmdreq); } @@ -260,6 +265,15 @@ static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) rc); goto rst_gpio_err; } + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { + rc = gpio_request(ctrl_pdata->avdd_en_gpio, + "avdd_enable"); + if (rc) { + pr_err("request avdd_en gpio failed, rc=%d\n", + rc); + goto avdd_en_gpio_err; + } + } if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { rc = gpio_request(ctrl_pdata->lcd_mode_sel_gpio, "mode_sel"); if (rc) { @@ -272,6 +286,9 @@ static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) return rc; lcd_mode_sel_gpio_err: + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) + gpio_free(ctrl_pdata->avdd_en_gpio); +avdd_en_gpio_err: gpio_free(ctrl_pdata->rst_gpio); rst_gpio_err: if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) @@ -424,6 +441,21 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) if (pdata->panel_info.rst_seq[++i]) usleep_range(pinfo->rst_seq[i] * 1000, pinfo->rst_seq[i] * 1000); } + + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { + if (ctrl_pdata->avdd_en_gpio_invert) { + rc = gpio_direction_output( + ctrl_pdata->avdd_en_gpio, 0); + } else { + rc = gpio_direction_output( + ctrl_pdata->avdd_en_gpio, 1); + } + if (rc) { + pr_err("%s: unable to set dir for avdd_en gpio\n", + __func__); + goto exit; + } + } } if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { @@ -452,6 +484,14 @@ int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) pr_debug("%s: Reset panel done\n", __func__); } } else { + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { + if (ctrl_pdata->avdd_en_gpio_invert) + gpio_set_value((ctrl_pdata->avdd_en_gpio), 1); + else + gpio_set_value((ctrl_pdata->avdd_en_gpio), 0); + + gpio_free(ctrl_pdata->avdd_en_gpio); + } if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { gpio_set_value((ctrl_pdata->disp_en_gpio), 0); gpio_free(ctrl_pdata->disp_en_gpio); @@ -2378,6 +2418,13 @@ int mdss_panel_parse_bl_settings(struct device_node *np, } } else if (!strcmp(data, "bl_ctrl_dcs")) { ctrl_pdata->bklt_ctrl = BL_DCS_CMD; + data = of_get_property(np, + "qcom,mdss-dsi-bl-dcs-command-state", NULL); + if (data && !strcmp(data, "dsi_hs_mode")) + ctrl_pdata->bklt_dcs_op_mode = DSI_HS_MODE; + else + ctrl_pdata->bklt_dcs_op_mode = DSI_LP_MODE; + pr_debug("%s: Configured DCS_CMD bklt ctrl\n", __func__); } diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index 40a79c4af38e..fe1289633291 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -1192,11 +1192,24 @@ static int mdss_fb_init_panel_modes(struct msm_fb_data_type *mfd, if (pdata->next) { spt = mdss_panel_get_timing_by_name(pdata->next, modedb[i].name); - if (!IS_ERR_OR_NULL(spt)) + /* for split config, recalculate xres and pixel clock */ + if (!IS_ERR_OR_NULL(spt)) { + unsigned long pclk, h_total, v_total; modedb[i].xres += spt->xres; - else + h_total = modedb[i].xres + + modedb[i].left_margin + + modedb[i].right_margin + + modedb[i].hsync_len; + v_total = modedb[i].yres + + modedb[i].lower_margin + + modedb[i].upper_margin + + modedb[i].vsync_len; + pclk = h_total * v_total * modedb[i].refresh; + modedb[i].pixclock = KHZ2PICOS(pclk / 1000); + } else { pr_debug("no matching split config for %s\n", modedb[i].name); + } /* * if no panel timing found for current, need to @@ -3607,6 +3620,16 @@ static void mdss_fb_var_to_panelinfo(struct fb_var_screeninfo *var, */ if (pinfo->is_dba_panel) pinfo->mipi.dsi_pclk_rate = pinfo->clk_rate; + + if (var->sync & FB_SYNC_HOR_HIGH_ACT) + pinfo->lcdc.h_polarity = 0; + else + pinfo->lcdc.h_polarity = 1; + + if (var->sync & FB_SYNC_VERT_HIGH_ACT) + pinfo->lcdc.v_polarity = 0; + else + pinfo->lcdc.v_polarity = 1; } void mdss_panelinfo_to_fb_var(struct mdss_panel_info *pinfo, @@ -4911,6 +4934,15 @@ static int __ioctl_wait_idle(struct msm_fb_data_type *mfd, u32 cmd) return ret; } +static bool check_not_supported_ioctl(u32 cmd) +{ + return((cmd == MSMFB_OVERLAY_SET) || (cmd == MSMFB_OVERLAY_UNSET) || + (cmd == MSMFB_OVERLAY_GET) || (cmd == MSMFB_OVERLAY_PREPARE) || + (cmd == MSMFB_DISPLAY_COMMIT) || (cmd == MSMFB_OVERLAY_PLAY) || + (cmd == MSMFB_BUFFER_SYNC) || (cmd == MSMFB_OVERLAY_QUEUE) || + (cmd == MSMFB_NOTIFY_UPDATE)); +} + /* * mdss_fb_do_ioctl() - MDSS Framebuffer ioctl function * @info: pointer to framebuffer info @@ -4945,6 +4977,11 @@ int mdss_fb_do_ioctl(struct fb_info *info, unsigned int cmd, if (!pdata || pdata->panel_info.dynamic_switch_pending) return -EPERM; + if (check_not_supported_ioctl(cmd)) { + pr_err("Unsupported ioctl\n"); + return -EINVAL; + } + atomic_inc(&mfd->ioctl_ref_cnt); mdss_fb_power_setting_idle(mfd); diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h index 518c24810acd..6e52390c2886 100644 --- a/drivers/video/fbdev/msm/mdss_fb.h +++ b/drivers/video/fbdev/msm/mdss_fb.h @@ -243,8 +243,8 @@ struct msm_mdp_interface { do_div(out, 2 * max_bright);\ } while (0) #define MDSS_BL_TO_BRIGHT(out, v, bl_max, max_bright) do {\ - out = ((v) * (max_bright));\ - do_div(out, bl_max);\ + out = (2 * ((v) * (max_bright)) + (bl_max));\ + do_div(out, 2 * bl_max);\ } while (0) struct mdss_fb_file_info { diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 102c22cba7dd..a953dd1a2ac2 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -62,11 +62,6 @@ #define EDID_VENDOR_ID_SIZE 4 #define EDID_IEEE_REG_ID 0x0c03 -enum edid_sink_mode { - SINK_MODE_DVI, - SINK_MODE_HDMI -}; - enum luminance_value { NO_LUMINANCE_DATA = 3, MAXIMUM_LUMINANCE = 4, @@ -474,8 +469,10 @@ static ssize_t hdmi_edid_sysfs_wta_res_info(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rc, page_id; + u32 i = 0, j, page; ssize_t ret = strnlen(buf, PAGE_SIZE); struct hdmi_edid_ctrl *edid_ctrl = hdmi_edid_get_ctrl(dev); + struct msm_hdmi_mode_timing_info info = {0}; if (!edid_ctrl) { DEV_ERR("%s: invalid input\n", __func__); @@ -488,7 +485,22 @@ static ssize_t hdmi_edid_sysfs_wta_res_info(struct device *dev, return rc; } - edid_ctrl->page_id = page_id; + if (page_id > MSM_HDMI_INIT_RES_PAGE) { + page = MSM_HDMI_INIT_RES_PAGE; + while (page < page_id) { + j = 1; + while (sizeof(info) * j < PAGE_SIZE) { + i++; + j++; + } + page++; + } + } + + if (i < HDMI_VFRMT_MAX) + edid_ctrl->page_id = page_id; + else + DEV_ERR("%s: invalid page id\n", __func__); DEV_DBG("%s: %d\n", __func__, edid_ctrl->page_id); return ret; @@ -2406,7 +2418,7 @@ end: return scaninfo; } /* hdmi_edid_get_sink_scaninfo */ -static u32 hdmi_edid_get_sink_mode(void *input) +u32 hdmi_edid_get_sink_mode(void *input, u32 mode) { struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; bool sink_mode; @@ -2419,8 +2431,13 @@ static u32 hdmi_edid_get_sink_mode(void *input) if (edid_ctrl->edid_override && (edid_ctrl->override_data.sink_mode != -1)) sink_mode = edid_ctrl->override_data.sink_mode; - else - sink_mode = edid_ctrl->sink_mode; + else { + if (edid_ctrl->sink_mode && + (mode > 0 && mode <= HDMI_EVFRMT_END)) + sink_mode = SINK_MODE_HDMI; + else + sink_mode = SINK_MODE_DVI; + } return sink_mode; } /* hdmi_edid_get_sink_mode */ @@ -2435,10 +2452,21 @@ static u32 hdmi_edid_get_sink_mode(void *input) */ bool hdmi_edid_is_dvi_mode(void *input) { - if (hdmi_edid_get_sink_mode(input)) - return false; - else + struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; + int sink_mode; + + if (!edid_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); return true; + } + + if (edid_ctrl->edid_override && + (edid_ctrl->override_data.sink_mode != -1)) + sink_mode = edid_ctrl->override_data.sink_mode; + else + sink_mode = edid_ctrl->sink_mode; + + return (sink_mode == SINK_MODE_DVI); } /** diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h index af802bb45f89..c604d0fbf7b2 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h @@ -58,10 +58,16 @@ struct hdmi_edid_override_data { int vic; }; +enum edid_sink_mode { + SINK_MODE_DVI, + SINK_MODE_HDMI +}; + int hdmi_edid_parser(void *edid_ctrl); u32 hdmi_edid_get_raw_data(void *edid_ctrl, u8 *buf, u32 size); u8 hdmi_edid_get_sink_scaninfo(void *edid_ctrl, u32 resolution); bool hdmi_edid_is_dvi_mode(void *input); +u32 hdmi_edid_get_sink_mode(void *edid_ctrl, u32 mode); bool hdmi_edid_sink_scramble_override(void *input); bool hdmi_edid_get_sink_scrambler_support(void *input); bool hdmi_edid_get_scdc_support(void *input); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 07592fa26a49..2e267f2695d7 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -66,7 +66,7 @@ #define HDMI_TX_4_MAX_PCLK_RATE 600000 #define hdmi_tx_get_fd(x) ((x && (ffs(x) > 0)) ? \ - hdmi_ctrl->feature_data[ffs(x) - 1] : 0) + hdmi_ctrl->feature_data[ffs(x) - 1] : NULL) #define hdmi_tx_set_fd(x, y) {if (x && (ffs(x) > 0)) \ hdmi_ctrl->feature_data[ffs(x) - 1] = y; } @@ -375,9 +375,12 @@ static void hdmi_tx_audio_setup(struct hdmi_tx_ctrl *hdmi_ctrl) } } -static inline u32 hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl) +static inline bool hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl) { - return hdmi_edid_is_dvi_mode(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID)); + void *data = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID); + + return (hdmi_edid_get_sink_mode(data, + hdmi_ctrl->vic) == SINK_MODE_DVI); } /* hdmi_tx_is_dvi_mode */ static inline u32 hdmi_tx_is_in_splash(struct hdmi_tx_ctrl *hdmi_ctrl) @@ -578,7 +581,8 @@ static ssize_t hdmi_tx_sysfs_wta_edid(struct device *dev, } mutex_lock(&hdmi_ctrl->tx_lock); - if (edid_size < EDID_BLOCK_SIZE) { + if ((edid_size < EDID_BLOCK_SIZE) || + (edid_size > hdmi_ctrl->edid_buf_size)) { DEV_DBG("%s: disabling custom edid\n", __func__); ret = -EINVAL; @@ -630,6 +634,11 @@ static ssize_t hdmi_tx_sysfs_rda_edid(struct device *dev, mutex_lock(&hdmi_ctrl->tx_lock); cea_blks = hdmi_ctrl->edid_buf[EDID_BLOCK_SIZE - 2]; + if (cea_blks >= MAX_EDID_BLOCKS) { + DEV_ERR("%s: invalid cea blocks\n", __func__); + mutex_unlock(&hdmi_ctrl->tx_lock); + return -EINVAL; + } size = (cea_blks + 1) * EDID_BLOCK_SIZE; size = min_t(u32, size, PAGE_SIZE); @@ -2156,6 +2165,8 @@ static int hdmi_tx_init_panel_info(struct hdmi_tx_ctrl *hdmi_ctrl) pinfo->lcdc.v_front_porch = timing.front_porch_v; pinfo->lcdc.v_pulse_width = timing.pulse_width_v; pinfo->lcdc.frame_rate = timing.refresh_rate; + pinfo->lcdc.h_polarity = timing.active_low_h; + pinfo->lcdc.v_polarity = timing.active_low_v; pinfo->type = DTV_PANEL; pinfo->pdest = DISPLAY_3; @@ -2442,6 +2453,7 @@ static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on) struct dss_io_data *io = NULL; /* Defaults: Disable block, HDMI mode */ u32 hdmi_ctrl_reg = BIT(1); + void *data = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID); if (!hdmi_ctrl) { DEV_ERR("%s: invalid input\n", __func__); @@ -2470,7 +2482,8 @@ static void hdmi_tx_set_mode(struct hdmi_tx_ctrl *hdmi_ctrl, u32 power_on) hdmi_ctrl_reg |= BIT(2); /* Set transmission mode to DVI based in EDID info */ - if (hdmi_edid_is_dvi_mode(hdmi_tx_get_fd(HDMI_TX_FEAT_EDID))) + if (hdmi_edid_get_sink_mode(data, + hdmi_ctrl->vic) == SINK_MODE_DVI) hdmi_ctrl_reg &= ~BIT(1); /* DVI mode */ /* @@ -2929,7 +2942,6 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev, { int rc = 0; struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev); - u32 is_mode_dvi; if (!hdmi_ctrl || !params) { DEV_ERR("%s: invalid input\n", __func__); @@ -2938,9 +2950,8 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev, mutex_lock(&hdmi_ctrl->tx_lock); - is_mode_dvi = hdmi_tx_is_dvi_mode(hdmi_ctrl); - - if (!is_mode_dvi && hdmi_tx_is_panel_on(hdmi_ctrl)) { + if (!hdmi_tx_is_dvi_mode(hdmi_ctrl) && + hdmi_tx_is_panel_on(hdmi_ctrl)) { memcpy(&hdmi_ctrl->audio_params, params, sizeof(struct msm_ext_disp_audio_setup_params)); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c index 102c2f994646..827013d06412 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_util.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c @@ -857,7 +857,7 @@ static int hdmi_ddc_read_retry(struct hdmi_tx_ddc_ctrl *ddc_ctrl) u32 reg_val, ndx, time_out_count, wait_time; struct hdmi_tx_ddc_data *ddc_data; int status; - int busy_wait_us; + int busy_wait_us = 0; if (!ddc_ctrl || !ddc_ctrl->io) { pr_err("invalid input\n"); @@ -1335,7 +1335,7 @@ int hdmi_ddc_write(struct hdmi_tx_ddc_ctrl *ddc_ctrl) u32 time_out_count; struct hdmi_tx_ddc_data *ddc_data; u32 wait_time; - int busy_wait_us; + int busy_wait_us = 0; if (!ddc_ctrl || !ddc_ctrl->io) { pr_err("invalid input\n"); diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index d88d87bd2092..6936c4c1f3cc 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -2180,7 +2180,6 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) mdss_set_quirk(mdata, MDSS_QUIRK_MDP_CLK_SET_RATE); mdata->has_wb_ubwc = true; set_bit(MDSS_CAPS_10_BIT_SUPPORTED, mdata->mdss_caps_map); - set_bit(MDSS_CAPS_AVR_SUPPORTED, mdata->mdss_caps_map); set_bit(MDSS_CAPS_SEC_DETACH_SMMU, mdata->mdss_caps_map); mdss_set_quirk(mdata, MDSS_QUIRK_HDR_SUPPORT_ENABLED); break; diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index cd403f19c088..feea8986af91 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -586,6 +586,7 @@ struct mdss_mdp_ctl { struct mdss_mdp_avr_info avr_info; bool commit_in_progress; struct mutex ds_lock; + bool need_vsync_on; }; struct mdss_mdp_mixer { @@ -1107,9 +1108,9 @@ static inline enum mdss_mdp_pu_type mdss_mdp_get_pu_type( if (!is_split_lm(mctl->mfd) || mdss_mdp_is_both_lm_valid(mctl)) pu_type = MDSS_MDP_DEFAULT_UPDATE; - else if (mctl->mixer_left->valid_roi) + else if (mctl->mixer_left && mctl->mixer_left->valid_roi) pu_type = MDSS_MDP_LEFT_ONLY_UPDATE; - else if (mctl->mixer_right->valid_roi) + else if (mctl->mixer_right && mctl->mixer_right->valid_roi) pu_type = MDSS_MDP_RIGHT_ONLY_UPDATE; else pr_err("%s: invalid pu_type\n", __func__); diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index 929eeb270f32..efd681a5d954 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -571,6 +571,9 @@ static u32 __calc_qseed3_mdp_clk_rate(struct mdss_mdp_pipe *pipe, u64 ver_dwnscale; u64 active_line; u64 backfill_line; + struct mdss_mdp_ctl *ctl = pipe->mixer_left->ctl; + u64 pclk_rate; + struct mdss_panel_info *pinfo = &ctl->panel_data->panel_info; ver_dwnscale = (u64)src_h << PHASE_STEP_SHIFT; do_div(ver_dwnscale, dst.h); @@ -596,12 +599,26 @@ static u32 __calc_qseed3_mdp_clk_rate(struct mdss_mdp_pipe *pipe, total_cycle = active_line_cycle + backfill_cycle; + /* + * MDP clkrate = total_cycle * PixelClock / Dest-width + * if pixelClock not available: + * = total_cycle * fps * v_total + */ + if ((pinfo->type == MIPI_CMD_PANEL) && dst.w) { + pclk_rate = (u64)mdss_panel_get_htotal(pinfo, false) * + v_total * fps; + do_div(pclk_rate, pinfo->xres); + total_cycle *= pclk_rate; + } else { + total_cycle *= (fps * v_total); + } + pr_debug("line: active=%lld backfill=%lld vds=%lld\n", active_line, backfill_line, ver_dwnscale); pr_debug("cycle: total=%lld active=%lld backfill=%lld\n", total_cycle, active_line_cycle, backfill_cycle); - return (u32)total_cycle * (fps * v_total); + return (u32)total_cycle; } static inline bool __is_vert_downscaling(u32 src_h, @@ -659,6 +676,7 @@ static u32 get_pipe_mdp_clk_rate(struct mdss_mdp_pipe *pipe, if (flags & PERF_CALC_PIPE_APPLY_CLK_FUDGE) rate = mdss_mdp_clk_fudge_factor(mixer, rate); + rate = min(mdata->max_mdp_clk_rate, rate); return rate; } @@ -2495,6 +2513,7 @@ struct mdss_mdp_mixer *mdss_mdp_mixer_alloc( u32 nmixers_wb; u32 i; u32 nmixers; + u32 nmixers_active; struct mdss_mdp_mixer *mixer_pool = NULL; if (!ctl || !ctl->mdata) @@ -2508,10 +2527,21 @@ struct mdss_mdp_mixer *mdss_mdp_mixer_alloc( case MDSS_MDP_MIXER_TYPE_INTF: mixer_pool = ctl->mdata->mixer_intf; nmixers = nmixers_intf; + nmixers_active = nmixers; + + for (i = 0; i < nmixers; i++) { + mixer = mixer_pool + i; + if (mixer->ref_cnt) + nmixers_active--; + } + mixer = NULL; /* * try to reserve first layer mixer for write back if - * assertive display needs to be supported through wfd + * assertive display needs to be supported through wfd. + * For external displays(pluggable) and writeback avoid + * allocating mixers LM0 and LM1 which are allocated + * to primary display first. */ if (ctl->mdata->has_wb_ad && ctl->intf_num && ((ctl->panel_data->panel_info.type != MIPI_CMD_PANEL) || @@ -2523,6 +2553,10 @@ struct mdss_mdp_mixer *mdss_mdp_mixer_alloc( && (ctl->mdata->ndspp < nmixers)) { mixer_pool += ctl->mdata->ndspp; nmixers -= ctl->mdata->ndspp; + } else if ((ctl->panel_data->panel_info.is_pluggable) && + nmixers_active) { + mixer_pool += ctl->mdata->ndspp; + nmixers -= ctl->mdata->ndspp; } break; diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c index 2e017fe5ec02..747b4e3e2f81 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_cmd.c @@ -3859,12 +3859,24 @@ static int mdss_mdp_cmd_reconfigure(struct mdss_mdp_ctl *ctl, } ctl->switch_with_handoff = false; } + /* + * keep track of vsync, so it can be enabled as part + * of the post switch sequence + */ + if (ctl->vsync_handler.enabled) + ctl->need_vsync_on = true; mdss_mdp_ctl_stop(ctl, MDSS_PANEL_POWER_OFF); mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_DSI_DYNAMIC_SWITCH, (void *) mode, CTL_INTF_EVENT_FLAG_DEFAULT); } else { + if (ctl->need_vsync_on && + ctl->ops.add_vsync_handler) { + ctl->ops.add_vsync_handler(ctl, + &ctl->vsync_handler); + ctl->need_vsync_on = false; + } mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); } } diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index 587150bbc9fa..d9aaac4526ea 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -58,6 +58,8 @@ struct intf_timing_params { u32 v_front_porch; u32 hsync_pulse_width; u32 vsync_pulse_width; + u32 h_polarity; + u32 v_polarity; u32 border_clr; u32 underflow_clr; @@ -641,13 +643,8 @@ static int mdss_mdp_video_timegen_setup(struct mdss_mdp_ctl *ctl, display_hctl = (hsync_end_x << 16) | hsync_start_x; den_polarity = 0; - if (MDSS_INTF_HDMI == ctx->intf_type) { - hsync_polarity = p->yres >= 720 ? 0 : 1; - vsync_polarity = p->yres >= 720 ? 0 : 1; - } else { - hsync_polarity = 0; - vsync_polarity = 0; - } + hsync_polarity = p->h_polarity; + vsync_polarity = p->v_polarity; polarity_ctl = (den_polarity << 2) | /* DEN Polarity */ (vsync_polarity << 1) | /* VSYNC Polarity */ (hsync_polarity << 0); /* HSYNC Polarity */ @@ -2178,7 +2175,8 @@ static int mdss_mdp_video_ctx_setup(struct mdss_mdp_ctl *ctl, itp->width = dsc->pclk_per_line; itp->xres = dsc->pclk_per_line; } - + itp->h_polarity = pinfo->lcdc.h_polarity; + itp->v_polarity = pinfo->lcdc.v_polarity; itp->h_back_porch = pinfo->lcdc.h_back_porch; itp->h_front_porch = pinfo->lcdc.h_front_porch; itp->v_back_porch = pinfo->lcdc.v_back_porch; diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index 472f1e8e8e3b..e44edb8fbea7 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -741,14 +741,15 @@ static int __cursor_layer_check(struct msm_fb_data_type *mfd, struct mdss_data_type *mdata = mdss_mdp_get_mdata(); if ((layer->z_order != HW_CURSOR_STAGE(mdata)) + || layer->flags & MDP_LAYER_FLIP_LR || layer->src_rect.w > mdata->max_cursor_size || layer->src_rect.h > mdata->max_cursor_size || layer->src_rect.w != layer->dst_rect.w || layer->src_rect.h != layer->dst_rect.h || !mdata->ncursor_pipes) { - pr_err("Incorrect cursor configs for pipe:%d, cursor_pipes:%d, z_order:%d\n", + pr_err("Incorrect cursor configs for pipe:0x%x, ncursor_pipes:%d, z_order:%d, flags:0x%x\n", layer->pipe_ndx, mdata->ncursor_pipes, - layer->z_order); + layer->z_order, layer->flags); pr_err("src:{%d,%d,%d,%d}, dst:{%d,%d,%d,%d}\n", layer->src_rect.x, layer->src_rect.y, layer->src_rect.w, layer->src_rect.h, @@ -1691,11 +1692,24 @@ static struct mdss_mdp_pipe *__find_and_move_cleanup_pipe( enum mdss_mdp_pipe_rect rect_num) { struct mdss_mdp_pipe *pipe = NULL; + struct mdss_mdp_data *buf, *tmpbuf; if (__find_pipe_in_list(&mdp5_data->pipes_destroy, pipe_ndx, &pipe, rect_num)) { pr_debug("reuse destroy pipe id:%d ndx:%d rect:%d\n", pipe->num, pipe_ndx, rect_num); + /* + * Pipe is being moved from destroy list to used list. + * It is possible that the buffer which was attached to a pipe + * in destroy list hasn't been cleaned up yet. Mark the buffers + * as cleanup to make sure that they will be freed before the + * pipe is reused. + */ + list_for_each_entry_safe(buf, tmpbuf, &pipe->buf_queue, + pipe_list) { + buf->state = MDP_BUF_STATE_CLEANUP; + list_del_init(&buf->pipe_list); + } list_move(&pipe->list, &mdp5_data->pipes_used); } else if (__find_pipe_in_list(&mdp5_data->pipes_cleanup, pipe_ndx, &pipe, rect_num)) { @@ -1812,13 +1826,16 @@ static int __validate_secure_session(struct mdss_overlay_private *mdp5_data) pr_debug("pipe count:: secure display:%d non-secure:%d secure-vid:%d,secure-cam:%d\n", sd_pipes, nonsd_pipes, secure_vid_pipes, secure_cam_pipes); + MDSS_XLOG(mdss_get_sd_client_cnt(), sd_pipes, nonsd_pipes, + secure_vid_pipes, secure_cam_pipes); if (mdss_get_sd_client_cnt() && !mdp5_data->sd_enabled) { pr_err("Secure session already enabled for other client\n"); return -EINVAL; } - if ((sd_pipes) && + if (((sd_pipes) || (mdp5_data->ctl->is_video_mode && + mdss_get_sd_client_cnt())) && (nonsd_pipes || secure_vid_pipes || secure_cam_pipes)) { pr_err("non-secure layer validation request during secure display session\n"); @@ -2410,14 +2427,14 @@ static int __validate_layers(struct msm_fb_data_type *mfd, int layer_count = commit->input_layer_cnt; u32 ds_mode = 0; - struct mdss_mdp_pipe *pipe, *tmp, *left_blend_pipe; + struct mdss_mdp_pipe *pipe = NULL, *tmp, *left_blend_pipe; struct mdss_mdp_pipe *right_plist[MAX_PIPES_PER_LM] = {0}; struct mdss_mdp_pipe *left_plist[MAX_PIPES_PER_LM] = {0}; struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); struct mdss_data_type *mdata = mfd_to_mdata(mfd); struct mdss_mdp_mixer *mixer = NULL; - struct mdp_input_layer *layer, *layer_list; + struct mdp_input_layer *layer = NULL, *layer_list; struct mdss_mdp_validate_info_t *validate_info_list = NULL; bool is_single_layer = false, force_validate; enum layer_pipe_q pipe_q_type; diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 3c679877705d..953aeb95332b 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -2318,6 +2318,31 @@ set_roi: } /* + * For the pipe which is being used for Secure Display, + * cleanup the previously queued buffers. + */ +static void __overlay_cleanup_secure_pipe(struct msm_fb_data_type *mfd) +{ + struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); + struct mdss_mdp_pipe *pipe; + struct mdss_mdp_data *buf, *tmpbuf; + + list_for_each_entry(pipe, &mdp5_data->pipes_used, list) { + if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION) { + list_for_each_entry_safe(buf, tmpbuf, &pipe->buf_queue, + pipe_list) { + if (buf->state == MDP_BUF_STATE_ACTIVE) { + __pipe_buf_mark_cleanup(mfd, buf); + list_move(&buf->buf_list, + &mdp5_data->bufs_freelist); + mdss_mdp_overlay_buf_free(mfd, buf); + } + } + } + } +} + +/* * Check if there is any change in secure state and store it. */ static void __overlay_set_secure_transition_state(struct msm_fb_data_type *mfd) @@ -2393,9 +2418,19 @@ static int __overlay_secure_ctrl(struct msm_fb_data_type *mfd) if (mdp5_data->secure_transition_state == SD_NON_SECURE_TO_SECURE) { if (!mdss_get_sd_client_cnt()) { MDSS_XLOG(0x11); - /*wait for ping pong done */ - if (ctl->ops.wait_pingpong) + /* wait for ping pong done */ + if (ctl->ops.wait_pingpong) { mdss_mdp_display_wait4pingpong(ctl, true); + + /* + * For command mode panels, there will not be + * a NULL commit preceding secure display. If + * a pipe is reused for secure display, + * cleanup buffers in the secure pipe before + * detaching IOMMU. + */ + __overlay_cleanup_secure_pipe(mfd); + } /* * unmap the previous commit buffers before * transitioning to secure state @@ -4413,7 +4448,7 @@ static int mdss_mdp_hw_cursor_pipe_update(struct msm_fb_data_type *mfd, if (!mfd->cursor_buf && (cursor->set & FB_CUR_SETIMAGE)) { ret = mdss_smmu_dma_alloc_coherent(&pdev->dev, cursor_frame_size, (dma_addr_t *) &mfd->cursor_buf_phys, - &mfd->cursor_buf_iova, mfd->cursor_buf, + &mfd->cursor_buf_iova, &mfd->cursor_buf, GFP_KERNEL, MDSS_IOMMU_DOMAIN_UNSECURE); if (ret) { pr_err("can't allocate cursor buffer rc:%d\n", ret); @@ -4601,7 +4636,7 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd, if (!mfd->cursor_buf && (cursor->set & FB_CUR_SETIMAGE)) { ret = mdss_smmu_dma_alloc_coherent(&pdev->dev, cursor_frame_size, (dma_addr_t *) &mfd->cursor_buf_phys, - &mfd->cursor_buf_iova, mfd->cursor_buf, + &mfd->cursor_buf_iova, &mfd->cursor_buf, GFP_KERNEL, MDSS_IOMMU_DOMAIN_UNSECURE); if (ret) { pr_err("can't allocate cursor buffer rc:%d\n", ret); @@ -4649,7 +4684,7 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd, mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); - if (cursor->set & FB_CUR_SETIMAGE) { + if (mfd->cursor_buf && (cursor->set & FB_CUR_SETIMAGE)) { u32 cursor_addr; ret = copy_from_user(mfd->cursor_buf, img->data, img->width * img->height * 4); @@ -4987,9 +5022,10 @@ static int mdss_fb_get_metadata(struct msm_fb_data_type *mfd, ret = mdss_fb_get_hw_caps(mfd, &metadata->data.caps); break; case metadata_op_get_ion_fd: - if (mfd->fb_ion_handle) { + if (mfd->fb_ion_handle && mfd->fb_ion_client) { metadata->data.fbmem_ionfd = - dma_buf_fd(mfd->fbmem_buf, 0); + ion_share_dma_buf_fd(mfd->fb_ion_client, + mfd->fb_ion_handle); if (metadata->data.fbmem_ionfd < 0) pr_err("fd allocation failed. fd = %d\n", metadata->data.fbmem_ionfd); diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index 50c26047185c..9c2b1d42bd35 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -2783,7 +2783,7 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl, (ctl->mfd->panel_info->type != WRITEBACK_PANEL)); if (valid_mixers && (mixer_cnt <= mdata->nmax_concurrent_ad_hw) && - valid_ad_panel) { + valid_ad_panel && (info->pp_program_mask & PP_PROGRAM_AD)) { ret = mdss_mdp_ad_setup(ctl->mfd); if (ret < 0) pr_warn("ad_setup(disp%d) returns %d\n", disp_num, ret); @@ -3264,6 +3264,8 @@ int mdss_mdp_pp_overlay_init(struct msm_fb_data_type *mfd) pr_err("Invalid mfd %pK mdata %pK\n", mfd, mdata); return -EPERM; } + if (mfd->index >= (MDP_BLOCK_MAX - MDP_LOGICAL_BLOCK_DISP_0)) + return 0; if (mdata->nad_cfgs) mfd->mdp.ad_calc_bl = pp_ad_calc_bl; @@ -7641,6 +7643,13 @@ static int pp_mfd_release_all(struct msm_fb_data_type *mfd) { struct mdss_data_type *mdata = mdss_mdp_get_mdata(); int ret = 0; + if (!mfd || !mdata) { + pr_err("Invalid mfd %pK mdata %pK\n", mfd, mdata); + return -EPERM; + } + + if (mfd->index >= (MDP_BLOCK_MAX - MDP_LOGICAL_BLOCK_DISP_0)) + return ret; if (mdata->nad_cfgs) { ret = pp_mfd_ad_release_all(mfd); diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.h b/drivers/video/fbdev/msm/mdss_mdp_pp.h index 809c389e99e8..136e2d79787c 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.h +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -181,7 +181,7 @@ struct mdss_pp_res_type { struct mdp_hist_lut_data enhist_disp_cfg[MDSS_BLOCK_DISP_NUM]; struct mdp_dither_cfg_data dither_disp_cfg[MDSS_BLOCK_DISP_NUM]; struct mdp_gamut_cfg_data gamut_disp_cfg[MDSS_BLOCK_DISP_NUM]; - uint16_t gamut_tbl[MDSS_BLOCK_DISP_NUM][GAMUT_TOTAL_TABLE_SIZE]; + uint16_t gamut_tbl[MDSS_BLOCK_DISP_NUM][GAMUT_TOTAL_TABLE_SIZE * 3]; u32 hist_data[MDSS_BLOCK_DISP_NUM][HIST_V_SIZE]; struct pp_sts_type pp_disp_sts[MDSS_BLOCK_DISP_NUM]; struct mdp_dither_cfg_data pa_dither_cfg[MDSS_BLOCK_DISP_NUM]; diff --git a/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c b/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c index 7cc94447efdc..d412a1c66e79 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c +++ b/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -150,7 +150,7 @@ static int mdss_mdp_splash_iommu_attach(struct msm_fb_data_type *mfd) { struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); struct mdss_data_type *mdata = mdss_mdp_get_mdata(); - int rc, ret; + int ret; /* * iommu dynamic attach for following conditions. @@ -167,26 +167,41 @@ static int mdss_mdp_splash_iommu_attach(struct msm_fb_data_type *mfd) return -EPERM; } - rc = mdss_smmu_map(MDSS_IOMMU_DOMAIN_UNSECURE, + /* + * Putting handoff pending to false to ensure smmu attach happens + * with early flag attribute + */ + mdata->handoff_pending = false; + + ret = mdss_smmu_set_attribute(MDSS_IOMMU_DOMAIN_UNSECURE, EARLY_MAP, 1); + if (ret) { + pr_debug("mdss set attribute failed for early map\n"); + goto end; + } + + ret = mdss_iommu_ctrl(1); + if (IS_ERR_VALUE(ret)) { + pr_err("mdss iommu attach failed\n"); + goto end; + } + + ret = mdss_smmu_map(MDSS_IOMMU_DOMAIN_UNSECURE, mdp5_data->splash_mem_addr, mdp5_data->splash_mem_addr, mdp5_data->splash_mem_size, IOMMU_READ | IOMMU_NOEXEC); - if (rc) { - pr_debug("iommu memory mapping failed rc=%d\n", rc); + if (ret) { + pr_err("iommu memory mapping failed ret=%d\n", ret); } else { - ret = mdss_iommu_ctrl(1); - if (IS_ERR_VALUE(ret)) { - pr_err("mdss iommu attach failed\n"); - mdss_smmu_unmap(MDSS_IOMMU_DOMAIN_UNSECURE, - mdp5_data->splash_mem_addr, - mdp5_data->splash_mem_size); - } else { - mfd->splash_info.iommu_dynamic_attached = true; - } + pr_debug("iommu map passed for PA=VA\n"); + mfd->splash_info.iommu_dynamic_attached = true; } - return rc; + ret = mdss_smmu_set_attribute(MDSS_IOMMU_DOMAIN_UNSECURE, EARLY_MAP, 0); +end: + mdata->handoff_pending = true; + + return ret; } static void mdss_mdp_splash_unmap_splash_mem(struct msm_fb_data_type *mfd) @@ -194,12 +209,10 @@ static void mdss_mdp_splash_unmap_splash_mem(struct msm_fb_data_type *mfd) struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd); if (mfd->splash_info.iommu_dynamic_attached) { - mdss_smmu_unmap(MDSS_IOMMU_DOMAIN_UNSECURE, mdp5_data->splash_mem_addr, mdp5_data->splash_mem_size); mdss_iommu_ctrl(0); - mfd->splash_info.iommu_dynamic_attached = false; } } diff --git a/drivers/video/fbdev/msm/mdss_mdp_util.c b/drivers/video/fbdev/msm/mdss_mdp_util.c index 22656175edf8..1ae3d0ba4ec6 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_util.c +++ b/drivers/video/fbdev/msm/mdss_mdp_util.c @@ -428,8 +428,8 @@ static int mdss_mdp_get_ubwc_plane_size(struct mdss_mdp_format_params *fmt, if (fmt->format == MDP_Y_CBCR_H2V2_UBWC || fmt->format == MDP_Y_CBCR_H2V2_TP10_UBWC) { - uint32_t y_stride_alignment, uv_stride_alignment; - uint32_t y_height_alignment, uv_height_alignment; + uint32_t y_stride_alignment = 0, uv_stride_alignment = 0; + uint32_t y_height_alignment = 0, uv_height_alignment = 0; uint32_t y_tile_width = fmt_ubwc->micro.tile_width; uint32_t y_tile_height = fmt_ubwc->micro.tile_height; uint32_t uv_tile_width = y_tile_width / 2; diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index 37b0ca7aa44b..e8255ff45726 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -406,9 +406,10 @@ struct lcd_panel_info { /* Pad height */ u32 yres_pad; u32 frame_rate; + u32 h_polarity; + u32 v_polarity; }; - /* DSI PHY configuration */ struct mdss_dsi_phy_ctrl { char regulator[7]; /* 8996, 1 * 5 */ diff --git a/drivers/video/fbdev/msm/mdss_rotator.c b/drivers/video/fbdev/msm/mdss_rotator.c index 399a12e3dcc8..61b0518d3ee6 100644 --- a/drivers/video/fbdev/msm/mdss_rotator.c +++ b/drivers/video/fbdev/msm/mdss_rotator.c @@ -687,7 +687,7 @@ static struct mdss_rot_hw_resource *mdss_rotator_hw_alloc( struct mdss_rot_hw_resource *hw; struct mdss_data_type *mdata = mdss_mdp_get_mdata(); u32 pipe_ndx, offset = mdss_mdp_get_wb_ctl_support(mdata, true); - int ret; + int ret = 0; hw = devm_kzalloc(&mgr->pdev->dev, sizeof(struct mdss_rot_hw_resource), GFP_KERNEL); diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c index 1b4765837c61..02e6d210b6e4 100644 --- a/drivers/video/fbdev/msm/mdss_smmu.c +++ b/drivers/video/fbdev/msm/mdss_smmu.c @@ -280,6 +280,27 @@ end: return rc; } +int mdss_smmu_set_attribute(int domain, int flag, int val) +{ + int rc = 0, domain_attr = 0; + struct mdss_smmu_client *mdss_smmu = mdss_smmu_get_cb(domain); + + if (!mdss_smmu) { + pr_err("not able to get smmu context\n"); + return -EINVAL; + } + + if (flag == EARLY_MAP) + domain_attr = DOMAIN_ATTR_EARLY_MAP; + else + goto end; + + rc = iommu_domain_set_attr(mdss_smmu->mmu_mapping->domain, + domain_attr, &val); +end: + return rc; +} + /* * mdss_smmu_attach_v2() * @@ -477,7 +498,7 @@ static void mdss_smmu_unmap_dma_buf_v2(struct sg_table *table, int domain, * bank device */ static int mdss_smmu_dma_alloc_coherent_v2(struct device *dev, size_t size, - dma_addr_t *phys, dma_addr_t *iova, void *cpu_addr, + dma_addr_t *phys, dma_addr_t *iova, void **cpu_addr, gfp_t gfp, int domain) { struct mdss_smmu_client *mdss_smmu = mdss_smmu_get_cb(domain); @@ -486,8 +507,8 @@ static int mdss_smmu_dma_alloc_coherent_v2(struct device *dev, size_t size, return -EINVAL; } - cpu_addr = dma_alloc_coherent(mdss_smmu->base.dev, size, iova, gfp); - if (!cpu_addr) { + *cpu_addr = dma_alloc_coherent(mdss_smmu->base.dev, size, iova, gfp); + if (!*cpu_addr) { pr_err("dma alloc coherent failed!\n"); return -ENOMEM; } diff --git a/drivers/video/fbdev/msm/mdss_smmu.h b/drivers/video/fbdev/msm/mdss_smmu.h index b1ee17a01c3f..679e564bcbc9 100644 --- a/drivers/video/fbdev/msm/mdss_smmu.h +++ b/drivers/video/fbdev/msm/mdss_smmu.h @@ -48,6 +48,11 @@ struct mdss_smmu_private { void mdss_smmu_register(struct device *dev); int mdss_smmu_init(struct mdss_data_type *mdata, struct device *dev); +int mdss_smmu_set_attribute(int domain, int flag, int val); + +enum smmu_attributes { + EARLY_MAP +}; static inline int mdss_smmu_dma_data_direction(int dir) { @@ -253,7 +258,7 @@ static inline void mdss_smmu_unmap_dma_buf(struct sg_table *table, int domain, } static inline int mdss_smmu_dma_alloc_coherent(struct device *dev, size_t size, - dma_addr_t *phys, dma_addr_t *iova, void *cpu_addr, + dma_addr_t *phys, dma_addr_t *iova, void **cpu_addr, gfp_t gfp, int domain) { struct mdss_data_type *mdata = mdss_mdp_get_mdata(); diff --git a/drivers/video/fbdev/msm/msm_dba/adv7533.c b/drivers/video/fbdev/msm/msm_dba/adv7533.c index 63e178d76403..09632b49d33b 100644 --- a/drivers/video/fbdev/msm/msm_dba/adv7533.c +++ b/drivers/video/fbdev/msm/msm_dba/adv7533.c @@ -517,13 +517,25 @@ static void adv7533_parse_vreg_dt(struct device *dev, } mp->vreg_config[i].disable_load = val_array[i]; - pr_debug("%s: %s min=%d, max=%d, enable=%d disable=%d\n", + /* post-on-sleep */ + memset(val_array, 0, sizeof(u32) * dt_vreg_total); + rc = of_property_read_u32_array(of_node, + "qcom,post-on-sleep", val_array, + dt_vreg_total); + if (rc) + pr_warn("%s: error read post on sleep. rc=%d\n", + __func__, rc); + else + mp->vreg_config[i].post_on_sleep = val_array[i]; + + pr_debug("%s: %s min=%d, max=%d, enable=%d disable=%d post-on-sleep=%d\n", __func__, mp->vreg_config[i].vreg_name, mp->vreg_config[i].min_voltage, mp->vreg_config[i].max_voltage, mp->vreg_config[i].enable_load, - mp->vreg_config[i].disable_load); + mp->vreg_config[i].disable_load, + mp->vreg_config[i].post_on_sleep); } devm_kfree(dev, val_array); diff --git a/drivers/video/msm/ba/Kconfig b/drivers/video/msm/ba/Kconfig new file mode 100644 index 000000000000..1ebc7eceeda6 --- /dev/null +++ b/drivers/video/msm/ba/Kconfig @@ -0,0 +1,12 @@ +# +# MSM BA V4L2 +# + +config MSM_BA_V4L2 + tristate "Qualcomm technologies Inc MSM V4L2 based BA driver" + depends on VIDEO_V4L2 + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + Enables support for the MSM V4L2 bridge abstraction diff --git a/drivers/video/msm/ba/Makefile b/drivers/video/msm/ba/Makefile new file mode 100644 index 000000000000..b4e7ddf3c79a --- /dev/null +++ b/drivers/video/msm/ba/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_MSM_BA_V4L2) += msm_v4l2_ba.o \ + msm_ba_common.o \ + msm_ba.o \ + msm_ba_debug.o + diff --git a/drivers/video/msm/ba/msm_ba.c b/drivers/video/msm/ba/msm_ba.c new file mode 100644 index 000000000000..8d1459088b80 --- /dev/null +++ b/drivers/video/msm/ba/msm_ba.c @@ -0,0 +1,892 @@ +/* + * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/msm_ba.h> + +#include "msm_ba_internal.h" +#include "msm_ba_debug.h" +#include "msm_ba_common.h" + +#define MSM_BA_DEV_NAME "msm_ba_8064" + +#define MSM_BA_MAX_EVENTS 10 + +int msm_ba_poll(void *instance, struct file *filp, + struct poll_table_struct *wait) +{ + struct msm_ba_inst *inst = instance; + int rc = 0; + + if (!inst) + return -EINVAL; + + poll_wait(filp, &inst->event_handler.wait, wait); + if (v4l2_event_pending(&inst->event_handler)) + rc |= POLLPRI; + + return rc; +} +EXPORT_SYMBOL(msm_ba_poll); + +int msm_ba_querycap(void *instance, struct v4l2_capability *cap) +{ + struct msm_ba_inst *inst = instance; + + if (!inst || !cap) { + dprintk(BA_ERR, + "Invalid input, inst = 0x%pK, cap = 0x%pK", inst, cap); + return -EINVAL; + } + + strlcpy(cap->driver, MSM_BA_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->card, MSM_BA_DEV_NAME, sizeof(cap->card)); + cap->bus_info[0] = 0; + cap->version = MSM_BA_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING; + memset(cap->reserved, 0x00, sizeof(cap->reserved)); + + return 0; +} +EXPORT_SYMBOL(msm_ba_querycap); + +int msm_ba_s_parm(void *instance, struct v4l2_streamparm *a) +{ + struct msm_ba_inst *inst = instance; + + if (!inst || !a) + return -EINVAL; + + return -EINVAL; +} +EXPORT_SYMBOL(msm_ba_s_parm); + +int msm_ba_enum_input(void *instance, struct v4l2_input *input) +{ + struct msm_ba_input *ba_input = NULL; + struct msm_ba_inst *inst = instance; + int rc = 0; + + if (!inst || !input) + return -EINVAL; + + if (input->index >= inst->dev_ctxt->num_inputs) + return -EINVAL; + + ba_input = msm_ba_find_input(input->index); + if (ba_input) { + input->type = V4L2_INPUT_TYPE_CAMERA; + input->std = V4L2_STD_ALL; + strlcpy(input->name, ba_input->name, sizeof(input->name)); + if (ba_input->input_type == BA_INPUT_HDMI || + ba_input->input_type == BA_INPUT_MHL) + input->capabilities = V4L2_IN_CAP_CUSTOM_TIMINGS; + else + input->capabilities = V4L2_IN_CAP_STD; + dprintk(BA_DBG, "msm_ba_find_input: name %s", input->name); + } + return rc; +} +EXPORT_SYMBOL(msm_ba_enum_input); + +int msm_ba_g_input(void *instance, unsigned int *index) +{ + struct msm_ba_inst *inst = instance; + struct msm_ba_input *ba_input = NULL; + int rc = 0; + + if (!inst || !index) + return -EINVAL; + + do { + /* First find current input */ + ba_input = msm_ba_find_input(inst->sd_input.index); + if (ba_input) { + if (ba_input->input_user_type == + BA_INPUT_USERTYPE_KERNEL) { + inst->sd_input.index++; + continue; + } + break; + } + } while (ba_input); + + if (ba_input) + *index = inst->sd_input.index; + else + rc = -ENOENT; + + return rc; +} +EXPORT_SYMBOL(msm_ba_g_input); + +int msm_ba_s_input(void *instance, unsigned int index) +{ + struct msm_ba_inst *inst = instance; + struct msm_ba_input *ba_input = NULL; + int rc = 0; + int rc_sig = 0; + + if (!inst) + return -EINVAL; + if (index > inst->dev_ctxt->num_inputs) + return -EINVAL; + + /* Find requested input */ + ba_input = msm_ba_find_input(index); + if (!ba_input) { + dprintk(BA_ERR, "Could not find input index: %d", index); + return -EINVAL; + } + if (!ba_input->sd) { + dprintk(BA_ERR, "No sd registered"); + return -EINVAL; + } + if (ba_input->in_use && + inst->event_handler.prio == V4L2_PRIORITY_RECORD) { + dprintk(BA_WARN, "Input %d in use", index); + return -EBUSY; + } + if (ba_input->ba_out_in_use) { + if (inst->ext_ops) { + if (inst->restore) { + dprintk(BA_DBG, "Stream off in set input: %d", + ba_input->bridge_chip_ip); + rc_sig = v4l2_subdev_call(ba_input->sd, + video, s_stream, 0); + if (rc_sig) + dprintk(BA_ERR, + "%s: Error in stream off. rc_sig %d", + __func__, rc_sig); + } + } else { + dprintk(BA_WARN, "Sd %d in use", ba_input->ba_out); + return -EBUSY; + } + } + rc = v4l2_subdev_call(ba_input->sd, video, s_routing, + ba_input->bridge_chip_ip, 0, 0); + if (rc) { + dprintk(BA_ERR, "Error: %d setting input: %d", + rc, ba_input->bridge_chip_ip); + return rc; + } + msm_ba_reset_ip_in_use_from_sd(ba_input->sd); + inst->sd_input.index = index; + strlcpy(inst->sd_input.name, ba_input->name, + sizeof(inst->sd_input.name)); + inst->sd = ba_input->sd; + ba_input->in_use = 1; + /* get current signal status */ + rc_sig = v4l2_subdev_call( + ba_input->sd, video, g_input_status, &ba_input->signal_status); + dprintk(BA_DBG, "Set input %s : %d - signal status: %d", + ba_input->name, index, ba_input->signal_status); + if (!rc_sig && !ba_input->signal_status) { + struct v4l2_event sd_event = { + .id = 0, + .type = V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK}; + int *ptr = (int *)sd_event.u.data; + ptr[0] = index; + ptr[1] = ba_input->signal_status; + msm_ba_queue_v4l2_event(inst, &sd_event); + } + return rc; +} +EXPORT_SYMBOL(msm_ba_s_input); + +int msm_ba_enum_output(void *instance, struct v4l2_output *output) +{ + struct msm_ba_input *ba_input = NULL; + struct msm_ba_inst *inst = instance; + int rc = 0; + + if (!inst || !output) + return -EINVAL; + + ba_input = msm_ba_find_output(output->index); + if (!ba_input) + return -EINVAL; + output->type = V4L2_OUTPUT_TYPE_ANALOG; + output->std = V4L2_STD_ALL; + strlcpy(output->name, ba_input->sd->name, sizeof(output->name)); + output->capabilities = V4L2_OUT_CAP_STD; + + return rc; +} +EXPORT_SYMBOL(msm_ba_enum_output); + +int msm_ba_g_output(void *instance, unsigned int *index) +{ + struct msm_ba_inst *inst = instance; + int rc = 0; + + if (!inst || !index) + return -EINVAL; + + *index = inst->sd_output.index; + + return rc; +} +EXPORT_SYMBOL(msm_ba_g_output); + +int msm_ba_s_output(void *instance, unsigned int index) +{ + struct msm_ba_inst *inst = instance; + struct msm_ba_input *ba_input = NULL; + int rc = 0; + + if (!inst) + return -EINVAL; + + ba_input = msm_ba_find_output(index); + if (ba_input) { + if (!ba_input->sd) { + dprintk(BA_ERR, "No sd registered"); + return -EINVAL; + } + ba_input->ba_out = index; + inst->sd_output.index = index; + inst->sd = ba_input->sd; + inst->sd_input.index = ba_input->ba_ip_idx; + } else { + dprintk(BA_ERR, "Could not find output index: %d", index); + rc = -EINVAL; + } + return rc; +} +EXPORT_SYMBOL(msm_ba_s_output); + +int msm_ba_enum_fmt(void *instance, struct v4l2_fmtdesc *f) +{ + struct msm_ba_inst *inst = instance; + + if (!inst || !f) + return -EINVAL; + + return -EINVAL; +} +EXPORT_SYMBOL(msm_ba_enum_fmt); + +int msm_ba_s_fmt(void *instance, struct v4l2_format *f) +{ + struct msm_ba_inst *inst = instance; + + if (!inst || !f) + return -EINVAL; + + return -EINVAL; +} +EXPORT_SYMBOL(msm_ba_s_fmt); + +int msm_ba_g_fmt(void *instance, struct v4l2_format *f) +{ + struct msm_ba_inst *inst = instance; + struct v4l2_subdev *sd = NULL; + struct msm_ba_input *ba_input = NULL; + v4l2_std_id new_std = V4L2_STD_UNKNOWN; + struct v4l2_dv_timings sd_dv_timings; + struct v4l2_subdev_format sd_fmt; + int rc = 0; + + if (!inst || !f) + return -EINVAL; + + sd = inst->sd; + if (!sd) { + dprintk(BA_ERR, "No sd registered"); + return -EINVAL; + } + ba_input = msm_ba_find_input(inst->sd_input.index); + if (!ba_input) { + dprintk(BA_ERR, "Could not find input index: %d", + inst->sd_input.index); + return -EINVAL; + } + if (ba_input->input_type != BA_INPUT_HDMI) { + rc = v4l2_subdev_call(sd, video, querystd, &new_std); + if (rc) { + dprintk(BA_ERR, "querystd failed %d for sd: %s", + rc, sd->name); + return -EINVAL; + } + inst->sd_input.std = new_std; + } else { + rc = v4l2_subdev_call(sd, video, g_dv_timings, &sd_dv_timings); + if (rc) { + dprintk(BA_ERR, "g_dv_timings failed %d for sd: %s", + rc, sd->name); + return -EINVAL; + } + } + + rc = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt); + if (rc) { + dprintk(BA_ERR, "get_fmt failed %d for sd: %s", + rc, sd->name); + } else { + f->fmt.pix.height = sd_fmt.format.height; + f->fmt.pix.width = sd_fmt.format.width; + switch (sd_fmt.format.code) { + case MEDIA_BUS_FMT_YUYV8_2X8: + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + break; + case MEDIA_BUS_FMT_YVYU8_2X8: + f->fmt.pix.pixelformat = V4L2_PIX_FMT_YVYU; + break; + case MEDIA_BUS_FMT_VYUY8_2X8: + f->fmt.pix.pixelformat = V4L2_PIX_FMT_VYUY; + break; + case MEDIA_BUS_FMT_UYVY8_2X8: + f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + break; + default: + dprintk(BA_ERR, "Unknown sd_mbus_fmt.code 0x%x", + sd_fmt.format.code); + f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + break; + } + } + return rc; +} +EXPORT_SYMBOL(msm_ba_g_fmt); + +int msm_ba_s_ctrl(void *instance, struct v4l2_control *control) +{ + struct msm_ba_inst *inst = instance; + + if (!inst || !control) + return -EINVAL; + + return v4l2_s_ctrl(NULL, &inst->ctrl_handler, control); +} +EXPORT_SYMBOL(msm_ba_s_ctrl); + +int msm_ba_g_ctrl(void *instance, struct v4l2_control *control) +{ + struct msm_ba_inst *inst = instance; + + if (!inst || !control) + return -EINVAL; + + return v4l2_g_ctrl(&inst->ctrl_handler, control); +} +EXPORT_SYMBOL(msm_ba_g_ctrl); + +int msm_ba_s_ext_ctrl(void *instance, struct v4l2_ext_controls *control) +{ + struct msm_ba_inst *inst = instance; + + if (!inst || !control) + return -EINVAL; + + return -EINVAL; +} +EXPORT_SYMBOL(msm_ba_s_ext_ctrl); + +int msm_ba_streamon(void *instance, enum v4l2_buf_type i) +{ + struct msm_ba_inst *inst = instance; + struct v4l2_subdev *sd = NULL; + int rc = 0; + + if (!inst) + return -EINVAL; + + sd = inst->sd; + if (!sd) { + dprintk(BA_ERR, "No sd registered"); + return -EINVAL; + } + rc = v4l2_subdev_call(sd, video, s_stream, 1); + if (rc) + dprintk(BA_ERR, "Stream on failed on input: %d", + inst->sd_input.index); + else + msm_ba_set_out_in_use(sd, 1); + + dprintk(BA_DBG, "Stream on: %s : %d", + inst->sd_input.name, inst->sd_input.index); + + return rc; +} +EXPORT_SYMBOL(msm_ba_streamon); + +int msm_ba_streamoff(void *instance, enum v4l2_buf_type i) +{ + struct msm_ba_inst *inst = instance; + struct v4l2_subdev *sd = NULL; + int rc = 0; + + if (!inst) + return -EINVAL; + + sd = inst->sd; + if (!sd) { + dprintk(BA_ERR, "No sd registered"); + return -EINVAL; + } + rc = v4l2_subdev_call(sd, video, s_stream, 0); + if (rc) + dprintk(BA_ERR, "Stream off failed on input: %d", + inst->sd_input.index); + + dprintk(BA_DBG, "Stream off: %s : %d", + inst->sd_input.name, inst->sd_input.index); + msm_ba_set_out_in_use(sd, 0); + return rc; +} +EXPORT_SYMBOL(msm_ba_streamoff); + +long msm_ba_private_ioctl(void *instance, int cmd, void *arg) +{ + long rc = 0; + struct msm_ba_inst *inst = instance; + struct v4l2_subdev *sd = NULL; + int *s_ioctl = arg; + + dprintk(BA_DBG, "Enter %s with command: 0x%x", __func__, cmd); + + if (!inst) + return -EINVAL; + + switch (cmd) { + case VIDIOC_HDMI_RX_CEC_S_LOGICAL: { + dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_S_LOGICAL"); + sd = inst->sd; + if (!sd) { + dprintk(BA_ERR, "No sd registered"); + return -EINVAL; + } + if (s_ioctl) { + rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl); + if (rc) + dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x", + __func__, rc, cmd); + } else { + dprintk(BA_ERR, "%s: NULL argument provided", __func__); + rc = -EINVAL; + } + } + break; + case VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL: { + dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_CLEAR_LOGICAL"); + sd = inst->sd; + if (!sd) { + dprintk(BA_ERR, "No sd registered"); + return -EINVAL; + } + rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl); + if (rc) + dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x", + __func__, rc, cmd); + } + break; + case VIDIOC_HDMI_RX_CEC_G_PHYSICAL: { + dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_G_PHYSICAL"); + sd = inst->sd; + if (!sd) { + dprintk(BA_ERR, "No sd registered"); + return -EINVAL; + } + if (s_ioctl) { + rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl); + if (rc) + dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x", + __func__, rc, cmd); + } else { + dprintk(BA_ERR, "%s: NULL argument provided", __func__); + rc = -EINVAL; + } + } + break; + case VIDIOC_HDMI_RX_CEC_G_CONNECTED: { + dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_G_CONNECTED"); + sd = inst->sd; + if (!sd) { + dprintk(BA_ERR, "No sd registered"); + return -EINVAL; + } + if (s_ioctl) { + rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl); + if (rc) + dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x", + __func__, rc, cmd); + } else { + dprintk(BA_ERR, "%s: NULL argument provided", __func__); + rc = -EINVAL; + } + } + break; + case VIDIOC_HDMI_RX_CEC_S_ENABLE: { + dprintk(BA_DBG, "VIDIOC_HDMI_RX_CEC_S_ENABLE"); + sd = inst->sd; + if (!sd) { + dprintk(BA_ERR, "No sd registered"); + return -EINVAL; + } + if (s_ioctl) { + rc = v4l2_subdev_call(sd, core, ioctl, cmd, s_ioctl); + if (rc) + dprintk(BA_ERR, "%s failed: %ld on cmd: 0x%x", + __func__, rc, cmd); + } else { + dprintk(BA_ERR, "%s: NULL argument provided", __func__); + rc = -EINVAL; + } + } + break; + default: + dprintk(BA_WARN, "Not a typewriter! Command: 0x%x", cmd); + rc = -ENOTTY; + break; + } + return rc; +} +EXPORT_SYMBOL(msm_ba_private_ioctl); + +int msm_ba_save_restore_input(void *instance, enum msm_ba_save_restore_ip sr) +{ + struct msm_ba_inst *inst = instance; + struct msm_ba_input *ba_input = NULL; + int rc = 0; + + if (!inst) + return -EINVAL; + + if (sr == BA_SR_RESTORE_IP && + inst->restore) { + dprintk(BA_DBG, "Restoring input: %d", + inst->saved_input); + rc = v4l2_subdev_call(inst->sd, video, s_routing, + inst->saved_input, 0, 0); + if (rc) + dprintk(BA_ERR, "Failed to restore input: %d", + inst->saved_input); + msm_ba_reset_ip_in_use_from_sd(inst->sd); + ba_input = msm_ba_find_input_from_sd(inst->sd, + inst->saved_input); + if (ba_input) + ba_input->in_use = 1; + else + dprintk(BA_WARN, "Could not find input %d from sd: %s", + inst->saved_input, inst->sd->name); + inst->restore = 0; + inst->saved_input = BA_IP_MAX; + dprintk(BA_DBG, "Stream on from save restore"); + rc = msm_ba_streamon(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + } else if (sr == BA_SR_SAVE_IP) { + ba_input = msm_ba_find_input(inst->sd_input.index); + if (ba_input == NULL) { + dprintk(BA_ERR, "Could not find input %d", + inst->sd_input.index); + } else if (ba_input->ba_out_in_use) { + inst->restore = 1; + inst->saved_input = + msm_ba_find_ip_in_use_from_sd(inst->sd); + if (inst->saved_input == BA_IP_MAX) { + dprintk(BA_ERR, "Could not find input to save"); + inst->restore = 0; + } + dprintk(BA_DBG, "Saving input: %d", + inst->saved_input); + rc = -EBUSY; + } + } else { + dprintk(BA_DBG, "Nothing to do in save and restore"); + } + return rc; +} +EXPORT_SYMBOL(msm_ba_save_restore_input); + +static void msm_ba_release_subdev_node(struct video_device *vdev) +{ + struct v4l2_subdev *sd = video_get_drvdata(vdev); + + sd->devnode = NULL; + kfree(vdev); +} + +static int msm_ba_register_v4l2_subdev(struct v4l2_device *v4l2_dev, + struct v4l2_subdev *sd) +{ + struct video_device *vdev; + int rc = 0; + + dprintk(BA_DBG, "Enter %s: v4l2_dev 0x%pK, v4l2_subdev 0x%pK", + __func__, v4l2_dev, sd); + if (NULL == v4l2_dev || NULL == sd || !sd->name[0]) { + dprintk(BA_ERR, "Invalid input"); + return -EINVAL; + } + rc = v4l2_device_register_subdev(v4l2_dev, sd); + if (rc < 0) { + dprintk(BA_ERR, + "%s(%d), V4L2 subdev register failed for %s rc: %d", + __func__, __LINE__, sd->name, rc); + return rc; + } + if (sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE) { + vdev = video_device_alloc(); + if (vdev == NULL) { + dprintk(BA_ERR, "%s Not enough memory", __func__); + return -ENOMEM; + } + video_set_drvdata(vdev, sd); + strlcpy(vdev->name, sd->name, sizeof(vdev->name)); + vdev->v4l2_dev = v4l2_dev; + vdev->fops = &v4l2_subdev_fops; + vdev->release = msm_ba_release_subdev_node; + rc = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, + sd->owner); + if (rc < 0) { + dprintk(BA_ERR, "%s Error registering video device %s", + __func__, sd->name); + kfree(vdev); + } else { +#if defined(CONFIG_MEDIA_CONTROLLER) + sd->entity.info.dev.major = VIDEO_MAJOR; + sd->entity.info.dev.minor = vdev->minor; + sd->entity.name = video_device_node_name(vdev); +#endif + sd->devnode = vdev; + } + } + dprintk(BA_DBG, "Exit %s with rc: %d", __func__, rc); + + return rc; +} + +int msm_ba_register_subdev_node(struct v4l2_subdev *sd) +{ + struct ba_ctxt *ba_ctxt; + int rc = 0; + + ba_ctxt = msm_ba_get_ba_context(); + rc = msm_ba_register_v4l2_subdev(&ba_ctxt->dev_ctxt->v4l2_dev, sd); + if (!rc) { + ba_ctxt->dev_ctxt->num_ba_subdevs++; + msm_ba_add_inputs(sd); + } + + return rc; +} +EXPORT_SYMBOL(msm_ba_register_subdev_node); + +static void __msm_ba_sd_unregister(struct v4l2_subdev *sub_dev) +{ + struct ba_ctxt *ba_ctxt; + + ba_ctxt = msm_ba_get_ba_context(); + mutex_lock(&ba_ctxt->ba_cs); + + v4l2_device_unregister_subdev(sub_dev); + ba_ctxt->dev_ctxt->num_ba_subdevs--; + msm_ba_del_inputs(sub_dev); + + dprintk(BA_DBG, "%s(%d), BA Unreg Sub Device : num ba devices %d : %s", + __func__, __LINE__, + ba_ctxt->dev_ctxt->num_ba_subdevs, sub_dev->name); + + mutex_unlock(&ba_ctxt->ba_cs); +} + +int msm_ba_unregister_subdev_node(struct v4l2_subdev *sub_dev) +{ + struct ba_ctxt *ba_ctxt; + + ba_ctxt = msm_ba_get_ba_context(); + if (!ba_ctxt || !ba_ctxt->dev_ctxt) + return -ENODEV; + if (!sub_dev) + return -EINVAL; + __msm_ba_sd_unregister(sub_dev); + + return 0; +} +EXPORT_SYMBOL(msm_ba_unregister_subdev_node); + +static int msm_ba_setup_event_queue(void *inst, + struct video_device *pvdev) +{ + int rc = 0; + struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst; + + v4l2_fh_init(&ba_inst->event_handler, pvdev); + v4l2_fh_add(&ba_inst->event_handler); + + return rc; +} + +int msm_ba_subscribe_event(void *inst, + const struct v4l2_event_subscription *sub) +{ + int rc = 0; + struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst; + + if (!inst || !sub) + return -EINVAL; + + rc = v4l2_event_subscribe(&ba_inst->event_handler, sub, + MSM_BA_MAX_EVENTS, NULL); + return rc; +} +EXPORT_SYMBOL(msm_ba_subscribe_event); + +int msm_ba_unsubscribe_event(void *inst, + const struct v4l2_event_subscription *sub) +{ + int rc = 0; + struct msm_ba_inst *ba_inst = (struct msm_ba_inst *)inst; + + if (!inst || !sub) + return -EINVAL; + + rc = v4l2_event_unsubscribe(&ba_inst->event_handler, sub); + return rc; +} +EXPORT_SYMBOL(msm_ba_unsubscribe_event); + +void msm_ba_subdev_event_hndlr(struct v4l2_subdev *sd, + unsigned int notification, void *arg) +{ + struct msm_ba_dev *dev_ctxt = NULL; + struct msm_ba_input *ba_input; + struct msm_ba_sd_event *ba_sd_event; + int bridge_chip_ip; + + if (!sd || !arg) { + dprintk(BA_ERR, "%s null v4l2 subdev or arg", __func__); + return; + } + + bridge_chip_ip = ((int *)((struct v4l2_event *)arg)->u.data)[0]; + ba_input = msm_ba_find_input_from_sd(sd, bridge_chip_ip); + if (!ba_input) { + dprintk(BA_WARN, "Could not find input %d from sd: %s", + bridge_chip_ip, sd->name); + return; + } + + ba_sd_event = kzalloc(sizeof(*ba_sd_event), GFP_KERNEL); + if (!ba_sd_event) { + dprintk(BA_ERR, "%s out of memory", __func__); + return; + } + + dev_ctxt = get_ba_dev(); + + ba_sd_event->sd_event = *(struct v4l2_event *)arg; + ((int *)ba_sd_event->sd_event.u.data)[0] = ba_input->ba_ip_idx; + mutex_lock(&dev_ctxt->dev_cs); + list_add_tail(&ba_sd_event->list, &dev_ctxt->sd_events); + mutex_unlock(&dev_ctxt->dev_cs); + + schedule_delayed_work(&dev_ctxt->sd_events_work, 0); +} + +void *msm_ba_open(const struct msm_ba_ext_ops *ext_ops) +{ + struct msm_ba_inst *inst = NULL; + struct msm_ba_dev *dev_ctxt = NULL; + int rc = 0; + + dev_ctxt = get_ba_dev(); + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + + if (!inst) { + dprintk(BA_ERR, "Failed to allocate memory"); + return NULL; + } + + mutex_init(&inst->inst_cs); + + init_waitqueue_head(&inst->kernel_event_queue); + inst->state = MSM_BA_DEV_UNINIT_DONE; + inst->dev_ctxt = dev_ctxt; + rc = msm_ba_ctrl_init(inst); + if (rc) { + dprintk(BA_WARN, "Failed to initialize controls: %d", rc); + msm_ba_ctrl_deinit(inst); + } + + if (!list_empty(&(inst->dev_ctxt->v4l2_dev.subdevs))) + inst->sd = list_first_entry(&(inst->dev_ctxt->v4l2_dev.subdevs), + struct v4l2_subdev, list); + + msm_ba_setup_event_queue(inst, dev_ctxt->vdev); + + mutex_lock(&dev_ctxt->dev_cs); + list_add_tail(&inst->list, &dev_ctxt->instances); + mutex_unlock(&dev_ctxt->dev_cs); + + dev_ctxt->state = BA_DEV_INIT; + dev_ctxt->state = BA_DEV_INIT_DONE; + inst->state = MSM_BA_DEV_INIT_DONE; + inst->sd_input.index = 0; + inst->event_handler.prio = V4L2_PRIORITY_DEFAULT; + + inst->debugfs_root = + msm_ba_debugfs_init_inst(inst, dev_ctxt->debugfs_root); + + inst->ext_ops = ext_ops; + + return inst; +} +EXPORT_SYMBOL(msm_ba_open); + +int msm_ba_close(void *instance) +{ + struct msm_ba_inst *inst = instance; + struct msm_ba_inst *temp; + struct msm_ba_dev *dev_ctxt; + struct list_head *ptr; + struct list_head *next; + int rc = 0; + + if (!inst) + return -EINVAL; + + dev_ctxt = inst->dev_ctxt; + mutex_lock(&dev_ctxt->dev_cs); + + list_for_each_safe(ptr, next, &dev_ctxt->instances) { + temp = list_entry(ptr, struct msm_ba_inst, list); + if (temp == inst) + list_del(&inst->list); + } + mutex_unlock(&dev_ctxt->dev_cs); + + msm_ba_ctrl_deinit(inst); + + v4l2_fh_del(&inst->event_handler); + v4l2_fh_exit(&inst->event_handler); + + debugfs_remove_recursive(inst->debugfs_root); + + dprintk(BA_DBG, "Closed BA instance: %pK", inst); + kfree(inst); + + return rc; +} +EXPORT_SYMBOL(msm_ba_close); diff --git a/drivers/video/msm/ba/msm_ba_common.c b/drivers/video/msm/ba/msm_ba_common.c new file mode 100644 index 000000000000..1306fca46652 --- /dev/null +++ b/drivers/video/msm/ba/msm_ba_common.c @@ -0,0 +1,647 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#include <linux/jiffies.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/kernel.h> + +#include "msm_ba_debug.h" +#include "msm_ba_common.h" + +static struct msm_ba_ctrl msm_ba_ctrls[] = { + { + .id = MSM_BA_PRIV_SD_NODE_ADDR, + .name = "Sub-device Node Address", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 16, + .default_value = 0, + .step = 2, + .menu_skip_mask = 0, + .flags = V4L2_CTRL_FLAG_VOLATILE, + .qmenu = NULL, + }, + { + .id = MSM_BA_PRIV_FPS, + .name = "FPS in Q16 format", + .type = V4L2_CTRL_TYPE_INTEGER, + .minimum = 0, + .maximum = 0x7fffffff, + .default_value = 60 << 16, + .step = 1, + .menu_skip_mask = 0, + .flags = V4L2_CTRL_FLAG_VOLATILE, + .qmenu = NULL, + }, +}; + +#define BA_NUM_CTRLS ARRAY_SIZE(msm_ba_ctrls) + +/* Assuming den is not zero, max 32 bits */ +#define BA_FRAC_TO_Q16(q, num, den) { \ + uint32_t pwr; \ + pwr = ilog2(den); \ + (q) = (num) << (16 - pwr); \ + } + +struct msm_ba_dev *get_ba_dev(void) +{ + struct ba_ctxt *ba_ctxt; + struct msm_ba_dev *dev_ctxt = NULL; + + ba_ctxt = msm_ba_get_ba_context(); + + mutex_lock(&ba_ctxt->ba_cs); + dev_ctxt = ba_ctxt->dev_ctxt; + mutex_unlock(&ba_ctxt->ba_cs); + + return dev_ctxt; +} + +void msm_ba_queue_v4l2_event(struct msm_ba_inst *inst, + struct v4l2_event *sd_event) +{ + v4l2_event_queue_fh(&inst->event_handler, sd_event); + wake_up(&inst->kernel_event_queue); +} + +static void msm_ba_print_event(struct v4l2_event *sd_event) +{ + switch (sd_event->type) { + case V4L2_EVENT_MSM_BA_PORT_SETTINGS_CHANGED: + dprintk(BA_DBG, "Port settings changed for ip_idx %d", + ((int *)sd_event->u.data)[0]); + break; + case V4L2_EVENT_MSM_BA_SIGNAL_IN_LOCK: + dprintk(BA_DBG, "Signal in lock for ip_idx %d", + ((int *)sd_event->u.data)[0]); + break; + case V4L2_EVENT_MSM_BA_SIGNAL_LOST_LOCK: + dprintk(BA_DBG, "Signal lost lock for ip_idx %d", + ((int *)sd_event->u.data)[0]); + break; + case V4L2_EVENT_MSM_BA_SOURCE_CHANGE: + dprintk(BA_DBG, "Video source change 0x%x", + ((int *)sd_event->u.data)[1]); + break; + case V4L2_EVENT_MSM_BA_HDMI_HPD: + dprintk(BA_DBG, "HDMI hotplug detected!"); + break; + case V4L2_EVENT_MSM_BA_HDMI_CEC_MESSAGE: + dprintk(BA_DBG, "HDMI CEC message!"); + break; + case V4L2_EVENT_MSM_BA_CP: + dprintk(BA_DBG, "Content protection detected!"); + break; + case V4L2_EVENT_MSM_BA_CABLE_DETECT: + dprintk(BA_DBG, "Cable detected: %d on ip_idx %d", + ((int *)sd_event->u.data)[1], + ((int *)sd_event->u.data)[0]); + break; + case V4L2_EVENT_MSM_BA_ERROR: + dprintk(BA_DBG, "Subdev error %d!", + ((int *)sd_event->u.data)[1]); + break; + default: + dprintk(BA_ERR, "Unknown event: 0x%x", sd_event->type); + break; + } +} + +static void msm_ba_signal_sessions_event(struct v4l2_event *sd_event) +{ + struct msm_ba_inst *inst = NULL; + struct msm_ba_dev *dev_ctxt = NULL; + int *ptr; + + msm_ba_print_event(sd_event); + dev_ctxt = get_ba_dev(); + ptr = (int *)sd_event->u.data; + + list_for_each_entry(inst, &(dev_ctxt->instances), list) { + if (inst->ext_ops && inst->ext_ops->msm_ba_cb) + inst->ext_ops->msm_ba_cb( + inst, sd_event->id, (void *)&ptr[1]); + else + msm_ba_queue_v4l2_event(inst, sd_event); + } +} + +void msm_ba_subdev_event_hndlr_delayed(struct work_struct *work) +{ + struct msm_ba_dev *dev_ctxt = NULL; + struct msm_ba_sd_event *ba_sd_event = NULL; + struct msm_ba_sd_event *ba_sd_event_tmp = NULL; + + dev_ctxt = get_ba_dev(); + + mutex_lock(&dev_ctxt->dev_cs); + if (!list_empty(&dev_ctxt->sd_events)) { + list_for_each_entry_safe(ba_sd_event, ba_sd_event_tmp, + &(dev_ctxt->sd_events), list) { + msm_ba_signal_sessions_event(&ba_sd_event->sd_event); + list_del(&ba_sd_event->list); + kfree(ba_sd_event); + break; + } + } else { + dprintk(BA_ERR, "%s - queue empty!!!", __func__); + } + mutex_unlock(&dev_ctxt->dev_cs); +} + +struct v4l2_subdev *msm_ba_sd_find(const char *name) +{ + struct v4l2_subdev *sd = NULL; + struct v4l2_subdev *sd_out = NULL; + struct msm_ba_dev *dev_ctxt = NULL; + + dev_ctxt = get_ba_dev(); + if (!list_empty(&(dev_ctxt->v4l2_dev.subdevs))) { + list_for_each_entry(sd, &(dev_ctxt->v4l2_dev.subdevs), list) + if (!strcmp(name, sd->name)) { + sd_out = sd; + break; + } + } + return sd_out; +} + +void msm_ba_add_inputs(struct v4l2_subdev *sd) +{ + struct msm_ba_input *input = NULL; + struct msm_ba_dev *dev_ctxt = NULL; + struct msm_ba_input_config *msm_ba_inp_cfg = NULL; + int i; + int str_length = 0; + int rc; + int start_index = 0; + int end_index = 0; + int dev_id = 0; + + dev_ctxt = get_ba_dev(); + if (!list_empty(&dev_ctxt->inputs)) + start_index = dev_ctxt->num_inputs; + + msm_ba_inp_cfg = dev_ctxt->msm_ba_inp_cfg; + dev_id = msm_ba_inp_cfg[start_index].ba_out; + end_index = dev_ctxt->num_config_inputs; + for (i = start_index; i < end_index; i++) { + str_length = strlen(msm_ba_inp_cfg[i].sd_name); + rc = memcmp(sd->name, msm_ba_inp_cfg[i].sd_name, str_length); + if (!rc && dev_id == msm_ba_inp_cfg[i].ba_out) { + input = kzalloc(sizeof(*input), GFP_KERNEL); + + if (!input) { + dprintk(BA_ERR, "Failed to allocate memory"); + break; + } + input->input_type = msm_ba_inp_cfg[i].input_type; + strlcpy(input->name, msm_ba_inp_cfg[i].name, + sizeof(input->name)); + input->bridge_chip_ip = msm_ba_inp_cfg[i].ba_ip; + input->ba_out = msm_ba_inp_cfg[i].ba_out; + input->ba_node_addr = msm_ba_inp_cfg[i].ba_node; + input->ba_ip_idx = i; + input->input_user_type = + msm_ba_inp_cfg[i].input_user_type; + input->sd = sd; + list_add_tail(&input->list, &dev_ctxt->inputs); + dev_ctxt->num_inputs++; + dprintk(BA_DBG, "Add input: name %s on %d", + input->name, input->ba_out); + } + } +} + +void msm_ba_del_inputs(struct v4l2_subdev *sd) +{ + struct msm_ba_input *input = NULL; + struct list_head *ptr; + struct list_head *next; + struct msm_ba_dev *dev_ctxt = NULL; + + dev_ctxt = get_ba_dev(); + + list_for_each_safe(ptr, next, &(dev_ctxt->inputs)) { + input = list_entry(ptr, struct msm_ba_input, list); + if (input->sd == sd) { + list_del(&input->list); + kfree(input); + } + } +} + +void msm_ba_set_out_in_use(struct v4l2_subdev *sd, int on) +{ + struct msm_ba_input *input = NULL; + struct msm_ba_dev *dev_ctxt = NULL; + + dev_ctxt = get_ba_dev(); + + if (!list_empty(&(dev_ctxt->inputs))) { + list_for_each_entry(input, &(dev_ctxt->inputs), list) + if (input->sd == sd) + input->ba_out_in_use = on; + } +} + +int msm_ba_find_ip_in_use_from_sd(struct v4l2_subdev *sd) +{ + struct msm_ba_input *input = NULL; + struct msm_ba_dev *dev_ctxt = NULL; + int ba_ip = BA_IP_MAX; + + dev_ctxt = get_ba_dev(); + + if (!list_empty(&(dev_ctxt->inputs))) { + list_for_each_entry(input, &(dev_ctxt->inputs), list) + if (input->sd == sd && + input->in_use) { + ba_ip = input->bridge_chip_ip; + break; + } + } + return ba_ip; +} + +void msm_ba_reset_ip_in_use_from_sd(struct v4l2_subdev *sd) +{ + struct msm_ba_input *input = NULL; + struct msm_ba_dev *dev_ctxt = NULL; + + dev_ctxt = get_ba_dev(); + + if (!list_empty(&(dev_ctxt->inputs))) { + list_for_each_entry(input, &(dev_ctxt->inputs), list) + if (input->sd == sd && + input->in_use) { + input->in_use = 0; + break; + } + } +} + +struct msm_ba_input *msm_ba_find_input_from_sd(struct v4l2_subdev *sd, + int bridge_chip_ip) +{ + struct msm_ba_input *input = NULL; + struct msm_ba_input *input_out = NULL; + struct msm_ba_dev *dev_ctxt = NULL; + + dev_ctxt = get_ba_dev(); + + if (!list_empty(&(dev_ctxt->inputs))) { + list_for_each_entry(input, &(dev_ctxt->inputs), list) + if (input->sd == sd && + input->bridge_chip_ip == bridge_chip_ip) { + input_out = input; + break; + } + } + return input_out; +} + +struct msm_ba_input *msm_ba_find_input(int ba_input_idx) +{ + struct msm_ba_input *input = NULL; + struct msm_ba_input *input_out = NULL; + struct msm_ba_dev *dev_ctxt = NULL; + + dev_ctxt = get_ba_dev(); + + if (!list_empty(&(dev_ctxt->inputs))) { + list_for_each_entry(input, &(dev_ctxt->inputs), list) + if (input->ba_ip_idx == ba_input_idx) { + input_out = input; + break; + } + } + return input_out; +} + +struct msm_ba_input *msm_ba_find_output(int ba_output) +{ + struct msm_ba_input *input = NULL; + struct msm_ba_input *input_out = NULL; + struct msm_ba_dev *dev_ctxt = NULL; + + dev_ctxt = get_ba_dev(); + + if (!list_empty(&(dev_ctxt->inputs))) { + list_for_each_entry(input, &(dev_ctxt->inputs), list) { + if (input->ba_out == ba_output) { + input_out = input; + break; + } + } + } + return input_out; +} + +int msm_ba_g_fps(void *instance, int *fps_q16) +{ + struct msm_ba_inst *inst = instance; + struct v4l2_subdev *sd = NULL; + struct v4l2_subdev_frame_interval sd_frame_int; + int rc = 0; + + if (!inst || !fps_q16) + return -EINVAL; + + sd = inst->sd; + if (!sd) { + dprintk(BA_ERR, "No sd registered"); + return -EINVAL; + } + rc = v4l2_subdev_call(sd, video, g_frame_interval, &sd_frame_int); + if (rc) { + dprintk(BA_ERR, "get frame interval failed %d for sd: %s", + rc, sd->name); + } else { + /* subdevice returns frame interval not fps! */ + if (sd_frame_int.interval.numerator) { + BA_FRAC_TO_Q16(*fps_q16, + sd_frame_int.interval.denominator, + sd_frame_int.interval.numerator); + } else { + *fps_q16 = + sd_frame_int.interval.denominator << 16; + } + } + return rc; +} + +static int msm_ba_try_get_ctrl(struct msm_ba_inst *inst, + struct v4l2_ctrl *ctrl) +{ + struct msm_ba_input *ba_input = NULL; + int rc = 0; + + if (!inst) { + dprintk(BA_ERR, "%s invalid parameters", __func__); + return -EINVAL; + } + + dprintk(BA_DBG, "%s ctrl->id: 0x%x", __func__, ctrl->id); + + switch (ctrl->id) { + case MSM_BA_PRIV_SD_NODE_ADDR: + ba_input = msm_ba_find_input(inst->sd_input.index); + if (ba_input) { + ctrl->val = ba_input->ba_node_addr; + dprintk(BA_DBG, + "%s: SD NODE ADDR ctrl->id:0x%x ctrl->val:%d", + __func__, ctrl->id, ctrl->val); + } else { + dprintk(BA_ERR, "%s Could not find input", + __func__); + rc = -EINVAL; + } + break; + case MSM_BA_PRIV_FPS: + rc = msm_ba_g_fps(inst, &ctrl->val); + break; + default: + dprintk(BA_ERR, "%s id: 0x%x not supported", + __func__, ctrl->id); + rc = -EINVAL; + break; + } + return rc; +} + +static int msm_ba_try_set_ctrl(struct msm_ba_inst *inst, + struct v4l2_ctrl *ctrl) +{ + struct msm_ba_input *ba_input = NULL; + int rc = 0; + + if (!inst) { + dprintk(BA_ERR, "%s invalid parameters", __func__); + return -EINVAL; + } + + dprintk(BA_DBG, "%s ctrl->id: 0x%x", __func__, ctrl->id); + + switch (ctrl->id) { + case MSM_BA_PRIV_SD_NODE_ADDR: + ba_input = msm_ba_find_input(inst->sd_input.index); + if (ba_input) { + ba_input->ba_node_addr = ctrl->val; + dprintk(BA_DBG, + "%s: SD NODE ADDR ctrl->id:0x%x node_addr:%d", + __func__, ctrl->id, ba_input->ba_node_addr); + } else { + dprintk(BA_ERR, "%s Could not find input", + __func__); + rc = -EINVAL; + } + break; + default: + dprintk(BA_ERR, "%s id: 0x%x not supported", + __func__, ctrl->id); + rc = -EINVAL; + break; + } + return rc; +} + +static int msm_ba_op_s_ctrl(struct v4l2_ctrl *ctrl) +{ + int rc = 0; + int c = 0; + struct msm_ba_inst *inst = container_of(ctrl->handler, + struct msm_ba_inst, ctrl_handler); + if (!inst) { + dprintk(BA_ERR, "%s invalid parameters", __func__); + return -EINVAL; + } + + for (c = 0; c < ctrl->ncontrols; ++c) { + if (ctrl->cluster[c]->is_new) { + rc = msm_ba_try_set_ctrl(inst, ctrl->cluster[c]); + if (rc) { + dprintk(BA_ERR, "Failed setting 0x%x", + ctrl->cluster[c]->id); + break; + } + } + } + return rc; +} + +static int msm_ba_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + int rc = 0; + int c = 0; + struct msm_ba_inst *inst = container_of(ctrl->handler, + struct msm_ba_inst, ctrl_handler); + struct v4l2_ctrl *master = ctrl->cluster[0]; + + for (c = 0; c < master->ncontrols; c++) { + if (master->cluster[c]->id == ctrl->id) { + rc = msm_ba_try_get_ctrl(inst, ctrl); + if (rc) { + dprintk(BA_ERR, "Failed getting 0x%x", + ctrl->id); + return rc; + } + } + } + return rc; +} + +static const struct v4l2_ctrl_ops msm_ba_ctrl_ops = { + + .g_volatile_ctrl = msm_ba_op_g_volatile_ctrl, + .s_ctrl = msm_ba_op_s_ctrl, +}; + +static struct v4l2_ctrl **msm_ba_get_super_cluster(struct msm_ba_inst *inst, + int *size) +{ + int c = 0; + int sz = 0; + struct v4l2_ctrl **cluster = NULL; + + if (!size || !inst) + return NULL; + + cluster = kmalloc(sizeof(struct v4l2_ctrl *) * + BA_NUM_CTRLS, GFP_KERNEL); + + if (!cluster) + return NULL; + + for (c = 0; c < BA_NUM_CTRLS; c++) + cluster[sz++] = inst->ctrls[c]; + + *size = sz; + return cluster; +} + +/* + * Controls init function. + * Caller is expected to call deinit in case of failure. + */ +int msm_ba_ctrl_init(struct msm_ba_inst *inst) +{ + int idx = 0; + struct v4l2_ctrl_config ctrl_cfg; + int rc = 0; + int cluster_size = 0; + + memset(&ctrl_cfg, 0x00, sizeof(struct v4l2_ctrl_config)); + + if (!inst) { + dprintk(BA_ERR, "%s - invalid instance", __func__); + return -EINVAL; + } + + inst->ctrls = kzalloc(sizeof(struct v4l2_ctrl *) * BA_NUM_CTRLS, + GFP_KERNEL); + if (!inst->ctrls) { + dprintk(BA_ERR, "%s - failed to allocate ctrl", __func__); + return -ENOMEM; + } + + rc = v4l2_ctrl_handler_init(&inst->ctrl_handler, BA_NUM_CTRLS); + + if (rc) { + dprintk(BA_ERR, "CTRL ERR: Control handler init failed, %d", + inst->ctrl_handler.error); + return rc; + } + for (; idx < BA_NUM_CTRLS; idx++) { + struct v4l2_ctrl *ctrl = NULL; + if (BA_IS_PRIV_CTRL(msm_ba_ctrls[idx].id)) { + /* add private control */ + ctrl_cfg.def = msm_ba_ctrls[idx].default_value; + ctrl_cfg.flags = 0; + ctrl_cfg.id = msm_ba_ctrls[idx].id; + ctrl_cfg.max = msm_ba_ctrls[idx].maximum; + ctrl_cfg.min = msm_ba_ctrls[idx].minimum; + ctrl_cfg.menu_skip_mask = + msm_ba_ctrls[idx].menu_skip_mask; + ctrl_cfg.name = msm_ba_ctrls[idx].name; + ctrl_cfg.ops = &msm_ba_ctrl_ops; + ctrl_cfg.step = msm_ba_ctrls[idx].step; + ctrl_cfg.type = msm_ba_ctrls[idx].type; + ctrl_cfg.qmenu = msm_ba_ctrls[idx].qmenu; + + ctrl = v4l2_ctrl_new_custom(&inst->ctrl_handler, + &ctrl_cfg, NULL); + } else { + if (msm_ba_ctrls[idx].type == V4L2_CTRL_TYPE_MENU) { + ctrl = v4l2_ctrl_new_std_menu( + &inst->ctrl_handler, + &msm_ba_ctrl_ops, + msm_ba_ctrls[idx].id, + msm_ba_ctrls[idx].maximum, + msm_ba_ctrls[idx].menu_skip_mask, + msm_ba_ctrls[idx].default_value); + } else { + ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, + &msm_ba_ctrl_ops, + msm_ba_ctrls[idx].id, + msm_ba_ctrls[idx].minimum, + msm_ba_ctrls[idx].maximum, + msm_ba_ctrls[idx].step, + msm_ba_ctrls[idx].default_value); + } + } + + if (!ctrl) { + dprintk(BA_ERR, "%s - invalid ctrl", __func__); + return -EINVAL; + } + + rc = inst->ctrl_handler.error; + if (rc) { + dprintk(BA_ERR, + "Error adding ctrl (%s) to ctrl handle, %d", + msm_ba_ctrls[idx].name, + inst->ctrl_handler.error); + return rc; + } + + switch (msm_ba_ctrls[idx].id) { + case MSM_BA_PRIV_SD_NODE_ADDR: + case MSM_BA_PRIV_FPS: + ctrl->flags |= msm_ba_ctrls[idx].flags; + break; + } + + inst->ctrls[idx] = ctrl; + } + + /* Construct a super cluster of all controls */ + inst->cluster = msm_ba_get_super_cluster(inst, &cluster_size); + if (!inst->cluster || !cluster_size) { + dprintk(BA_WARN, + "Failed to setup super cluster"); + return -EINVAL; + } + v4l2_ctrl_cluster(cluster_size, inst->cluster); + + return rc; +} + +void msm_ba_ctrl_deinit(struct msm_ba_inst *inst) +{ + kfree(inst->ctrls); + kfree(inst->cluster); + v4l2_ctrl_handler_free(&inst->ctrl_handler); +} diff --git a/drivers/video/msm/ba/msm_ba_common.h b/drivers/video/msm/ba/msm_ba_common.h new file mode 100644 index 000000000000..64ef2ab7537e --- /dev/null +++ b/drivers/video/msm/ba/msm_ba_common.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef _MSM_BA_COMMON_H_ +#define _MSM_BA_COMMON_H_ + +#include "msm_ba_internal.h" + +#define BA_IS_PRIV_CTRL(idx) (\ + (V4L2_CTRL_ID2CLASS(idx) == V4L2_CTRL_CLASS_USER) && \ + V4L2_CTRL_DRIVER_PRIV(idx)) + +struct msm_ba_dev *get_ba_dev(void); +void msm_ba_queue_v4l2_event(struct msm_ba_inst *inst, + struct v4l2_event *sd_event); +struct v4l2_subdev *msm_ba_sd_find(const char *name); +void msm_ba_add_inputs(struct v4l2_subdev *sd); +void msm_ba_del_inputs(struct v4l2_subdev *sd); +void msm_ba_set_out_in_use(struct v4l2_subdev *sd, int on); +int msm_ba_find_ip_in_use_from_sd(struct v4l2_subdev *sd); +void msm_ba_reset_ip_in_use_from_sd(struct v4l2_subdev *sd); +struct msm_ba_input *msm_ba_find_input_from_sd(struct v4l2_subdev *sd, + int bridge_chip_ip); +struct msm_ba_input *msm_ba_find_input(int ba_input_idx); +struct msm_ba_input *msm_ba_find_output(int ba_output); +int msm_ba_g_fps(void *instance, int *fps_q16); +int msm_ba_ctrl_init(struct msm_ba_inst *inst); +void msm_ba_ctrl_deinit(struct msm_ba_inst *inst); + +#endif diff --git a/drivers/video/msm/ba/msm_ba_debug.c b/drivers/video/msm/ba/msm_ba_debug.c new file mode 100644 index 000000000000..b57f4c94e18b --- /dev/null +++ b/drivers/video/msm/ba/msm_ba_debug.c @@ -0,0 +1,223 @@ +/* Copyright (c) 2012-2015,2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#include "msm_ba_debug.h" + +#define MAX_DBG_BUF_SIZE 1008 + +int msm_ba_debug = BA_ERR | BA_WARN; +int msm_ba_debug_out = BA_OUT_PRINTK; + +struct debug_buffer { + char ptr[MAX_DBG_BUF_SIZE]; + char *curr; + u32 filled_size; +}; + +#define INIT_DBG_BUF(__buf) ({ \ + __buf->curr = __buf->ptr;\ + __buf->filled_size = 0; \ +}) + +static int dev_info_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static u32 write_str(struct debug_buffer *buffer, const char *fmt, ...) +{ + va_list args; + u32 size = 0; + size_t buf_size = 0; + + if (MAX_DBG_BUF_SIZE - 1 > buffer->filled_size) { + buf_size = MAX_DBG_BUF_SIZE - 1 - buffer->filled_size; + va_start(args, fmt); + size = vscnprintf(buffer->curr, buf_size, fmt, args); + va_end(args); + buffer->curr += size; + buffer->filled_size += size; + } + return size; +} + +static ssize_t dev_info_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct msm_ba_dev *dev_ctxt = file->private_data; + struct debug_buffer *dbg_buf = NULL; + ssize_t size = 0; + + if (!dev_ctxt) { + dprintk(BA_ERR, "Invalid params, dev: 0x%pK", dev_ctxt); + return 0; + } + + dbg_buf = kmalloc(sizeof(struct debug_buffer), GFP_KERNEL); + if (dbg_buf == NULL) + return 0; + + INIT_DBG_BUF(dbg_buf); + write_str(dbg_buf, "==============================="); + write_str(dbg_buf, "DEV: 0x%pK", dev_ctxt); + write_str(dbg_buf, "==============================="); + write_str(dbg_buf, "state: %d", dev_ctxt->state); + + size = simple_read_from_buffer(buf, count, ppos, + dbg_buf->ptr, dbg_buf->filled_size); + + kfree(dbg_buf); + + return size; +} + +static const struct file_operations dev_info_fops = { + .open = dev_info_open, + .read = dev_info_read, +}; + +struct dentry *msm_ba_debugfs_init_drv(void) +{ + bool ok = false; + struct dentry *dir = debugfs_create_dir(BA_DBG_LABEL, NULL); + struct ba_ctxt *ba_ctxt; + + if (IS_ERR_OR_NULL(dir)) { + dir = NULL; + goto failed_create_dir; + } + +#define __debugfs_create(__type, __name, __value) ({ \ + struct dentry *f = debugfs_create_##__type(__name, S_IRUGO | S_IWUSR, \ + dir, __value); \ + if (IS_ERR_OR_NULL(f)) { \ + dprintk(BA_ERR, "Failed creating debugfs file '%pKd/%s'", \ + dir, __name); \ + f = NULL; \ + } \ + f; \ +}) + + ok = + __debugfs_create(x32, "debug_level", &msm_ba_debug) && + __debugfs_create(u32, "debug_output", &msm_ba_debug_out); + +#undef __debugfs_create + + if (!ok) + goto failed_create_dir; + + return dir; + +failed_create_dir: + if (dir) { + ba_ctxt = msm_ba_get_ba_context(); + debugfs_remove_recursive(ba_ctxt->debugfs_root); + } + return NULL; +} + +struct dentry *msm_ba_debugfs_init_dev(struct msm_ba_dev *dev_ctxt, + struct dentry *parent) +{ + struct dentry *dir = NULL; + char debugfs_name[MAX_DEBUGFS_NAME]; + + if (!dev_ctxt) { + dprintk(BA_ERR, "Invalid params, core: %pK", dev_ctxt); + goto failed_create_dir; + } + + snprintf(debugfs_name, MAX_DEBUGFS_NAME, "dev_%pK", dev_ctxt); + dir = debugfs_create_dir(debugfs_name, parent); + if (!dir) { + dprintk(BA_ERR, "Failed to create debugfs for msm_ba"); + goto failed_create_dir; + } + if (!debugfs_create_file("info", S_IRUGO, dir, dev_ctxt, + &dev_info_fops)) { + dprintk(BA_ERR, "debugfs_create_file: fail"); + goto failed_create_dir; + } +failed_create_dir: + return dir; +} + +static int inst_info_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t inst_info_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct msm_ba_inst *inst = file->private_data; + struct debug_buffer *dbg_buf = NULL; + ssize_t size = 0; + + if (!inst) { + dprintk(BA_ERR, "Invalid params, dev: %pK", inst); + return 0; + } + + dbg_buf = kmalloc(sizeof(struct debug_buffer), GFP_KERNEL); + if (dbg_buf == NULL) + return 0; + + INIT_DBG_BUF(dbg_buf); + write_str(dbg_buf, "==============================="); + write_str(dbg_buf, "INSTANCE: %pK (%s)", inst, + "BA device"); + write_str(dbg_buf, "==============================="); + write_str(dbg_buf, "dev: %pK", inst->dev_ctxt); + write_str(dbg_buf, "state: %d", inst->state); + + size = simple_read_from_buffer(buf, count, ppos, + dbg_buf->ptr, dbg_buf->filled_size); + + kfree(dbg_buf); + + return size; +} + +static const struct file_operations inst_info_fops = { + .open = inst_info_open, + .read = inst_info_read, +}; + +struct dentry *msm_ba_debugfs_init_inst(struct msm_ba_inst *inst, + struct dentry *parent) +{ + struct dentry *dir = NULL; + char debugfs_name[MAX_DEBUGFS_NAME]; + + if (!inst) { + dprintk(BA_ERR, "Invalid params, inst: %pK", inst); + goto failed_create_dir; + } + snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%pK", inst); + dir = debugfs_create_dir(debugfs_name, parent); + if (!dir) { + dprintk(BA_ERR, "Failed to create debugfs for msm_ba"); + goto failed_create_dir; + } + if (!debugfs_create_file("info", S_IRUGO, dir, inst, &inst_info_fops)) { + dprintk(BA_ERR, "debugfs_create_file: fail"); + goto failed_create_dir; + } + inst->debug.pdata[SESSION_INIT].sampling = true; +failed_create_dir: + return dir; +} diff --git a/drivers/video/msm/ba/msm_ba_debug.h b/drivers/video/msm/ba/msm_ba_debug.h new file mode 100644 index 000000000000..baabb712cc58 --- /dev/null +++ b/drivers/video/msm/ba/msm_ba_debug.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef __MSM_BA_DEBUG__ +#define __MSM_BA_DEBUG__ +#include <linux/debugfs.h> +#include <linux/delay.h> +#include "msm_ba_internal.h" + +#ifndef BA_DBG_LABEL +#define BA_DBG_LABEL "msm_ba" +#endif + +#define BA_DBG_TAG BA_DBG_LABEL "(%d): %4s: " + +/* To enable messages OR these values and + * echo the result to debugfs file. + * + * To enable all messages set debug_level = 0x001F + */ + +enum ba_msg_prio { + BA_ERR = 0x0001, + BA_WARN = 0x0002, + BA_INFO = 0x0004, + BA_DBG = 0x0008, + BA_PROF = 0x0010 +}; + +enum ba_msg_out { + BA_OUT_PRINTK = 0, + BA_OUT_FTRACE +}; + +extern int msm_ba_debug; +extern int msm_ba_debug_out; + +#define BA_MSG_PRIO2STRING(__level) ({ \ + char *__str; \ + \ + __str = (__level == BA_ERR ? "err" : \ + (__level == BA_WARN ? "warn" : \ + (__level == BA_INFO ? "info" : \ + (__level == BA_DBG ? "dbg" : \ + (__level == BA_PROF ? "prof" : "????"))))); \ + \ + __str; \ + }) + +#define dprintk(__level, __fmt, arg...) \ + do { \ + if (msm_ba_debug & __level) { \ + if (msm_ba_debug_out == BA_OUT_PRINTK) { \ + pr_info(BA_DBG_TAG __fmt "\n", \ + __LINE__, \ + BA_MSG_PRIO2STRING(__level), \ + ## arg); \ + } else if (msm_ba_debug_out == BA_OUT_FTRACE) { \ + trace_printk(KERN_DEBUG BA_DBG_TAG __fmt "\n", \ + __LINE__, \ + BA_MSG_PRIO2STRING(__level), \ + ## arg); \ + } \ + } \ + } while (0) + + +struct dentry *msm_ba_debugfs_init_drv(void); +struct dentry *msm_ba_debugfs_init_dev(struct msm_ba_dev *dev_ctxt, + struct dentry *parent); +struct dentry *msm_ba_debugfs_init_inst(struct msm_ba_inst *inst, + struct dentry *parent); + +#endif diff --git a/drivers/video/msm/ba/msm_ba_internal.h b/drivers/video/msm/ba/msm_ba_internal.h new file mode 100644 index 000000000000..bd52e8e400ce --- /dev/null +++ b/drivers/video/msm/ba/msm_ba_internal.h @@ -0,0 +1,220 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef _MSM_BA_INTERNAL_H_ +#define _MSM_BA_INTERNAL_H_ + +#include <linux/atomic.h> +#include <linux/list.h> +#include <linux/time.h> +#include <linux/types.h> +#include <linux/completion.h> +#include <linux/wait.h> +#include <linux/workqueue.h> +#include <linux/msm-bus.h> +#include <linux/msm-bus-board.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-event.h> +#include <media/v4l2-ctrls.h> +#include <media/videobuf2-core.h> +#include <media/msm_ba.h> + +#define MSM_BA_DRV_NAME "msm_ba_driver" + +#define MSM_BA_VERSION KERNEL_VERSION(0, 0, 1) + +#define MAX_NAME_LENGTH 64 + +#define MAX_DEBUGFS_NAME MAX_NAME_LENGTH + +#define DEFAULT_WIDTH 720 +#define DEFAULT_HEIGHT 507 + +enum ba_dev_state { + BA_DEV_UNINIT = 0, + BA_DEV_LOADED, + BA_DEV_INIT, + BA_DEV_INIT_DONE, + BA_DEV_INVALID +}; + +enum instance_state { + MSM_BA_DEV_UNINIT_DONE = 0x0001, + MSM_BA_DEV_INIT, + MSM_BA_DEV_INIT_DONE, + MSM_BA_OPEN, + MSM_BA_OPEN_DONE, + MSM_BA_START, + MSM_BA_START_DONE, + MSM_BA_STOP, + MSM_BA_STOP_DONE, + MSM_BA_CLOSE, + MSM_BA_CLOSE_DONE, + MSM_BA_DEV_UNINIT, + MSM_BA_DEV_INVALID +}; + +struct ba_ctxt { + struct mutex ba_cs; + struct msm_ba_dev *dev_ctxt; + struct dentry *debugfs_root; +}; + +enum profiling_points { + SYS_INIT = 0, + SESSION_INIT, + MAX_PROFILING_POINTS +}; + +struct profile_data { + int start; + int stop; + int cumulative; + char name[64]; + int sampling; + int average; +}; + +struct msm_ba_debug { + struct profile_data pdata[MAX_PROFILING_POINTS]; + int profile; + int samples; +}; + +struct msm_ba_dev_capability { + u32 capability_set; +}; + +enum msm_ba_ip_type { + BA_INPUT_CVBS = 0, + BA_INPUT_COMPONENT, + BA_INPUT_YC, + BA_INPUT_RGB, + BA_INPUT_HDMI, + BA_INPUT_MHL, + BA_INPUT_DVI, + BA_INPUT_TTL, + BA_INPUT_MAX = 0xffffffff +}; + +enum msm_ba_input_usr_type { + BA_INPUT_USERTYPE_KERNEL = 0, + BA_INPUT_USERTYPE_USER, + BA_INPUT_USERTYPE_MAX = 0xffffffff +}; + +struct msm_ba_input_config { + enum msm_ba_ip_type input_type; + const char *name; + int ba_ip; + int ba_out; + const char *sd_name; + int ba_node; + enum msm_ba_input_usr_type input_user_type; +}; + +struct msm_ba_sd_event { + struct list_head list; + struct v4l2_event sd_event; +}; + +struct msm_ba_input { + struct list_head list; + enum msm_ba_ip_type input_type; + unsigned int name_index; + char name[32]; + int bridge_chip_ip; + int ba_node_addr; + int ba_out; + int ba_ip_idx; + struct v4l2_subdev *sd; + int signal_status; + int in_use; + int ba_out_in_use; + enum msm_ba_input_usr_type input_user_type; +}; + +struct msm_ba_dev { + struct mutex dev_cs; + + enum ba_dev_state state; + + struct list_head inputs; + uint32_t num_inputs; + + /* V4L2 Framework */ + struct v4l2_device v4l2_dev; + struct video_device *vdev; + struct media_device mdev; + + struct list_head instances; + + /* BA v4l2 sub devs */ + uint32_t num_ba_subdevs; + struct list_head sd_events; + struct delayed_work sd_events_work; + + /* BA input config list */ + struct msm_ba_input_config *msm_ba_inp_cfg; + uint32_t num_config_inputs; + + struct dentry *debugfs_root; +}; + +struct msm_ba_inst { + struct list_head list; + struct mutex inst_cs; + struct msm_ba_dev *dev_ctxt; + + struct v4l2_input sd_input; + struct v4l2_output sd_output; + struct v4l2_subdev *sd; + int state; + int saved_input; + int restore; + + struct v4l2_fh event_handler; + wait_queue_head_t kernel_event_queue; + + struct v4l2_ctrl **cluster; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl **ctrls; + + struct msm_ba_debug debug; + struct dentry *debugfs_root; + + const struct msm_ba_ext_ops *ext_ops; +}; + +struct msm_ba_ctrl { + u32 id; + char name[MAX_NAME_LENGTH]; + enum v4l2_ctrl_type type; + s32 minimum; + s32 maximum; + s32 default_value; + u32 step; + u32 menu_skip_mask; + u32 flags; + const char * const *qmenu; +}; + +struct ba_ctxt *msm_ba_get_ba_context(void); + +void msm_ba_subdev_event_hndlr(struct v4l2_subdev *sd, + unsigned int notification, void *arg); +void msm_ba_subdev_event_hndlr_delayed(struct work_struct *work); + +#endif diff --git a/drivers/video/msm/ba/msm_v4l2_ba.c b/drivers/video/msm/ba/msm_v4l2_ba.c new file mode 100644 index 000000000000..89fc08dd3c33 --- /dev/null +++ b/drivers/video/msm/ba/msm_v4l2_ba.c @@ -0,0 +1,614 @@ +/* + * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/debugfs.h> +#include <linux/io.h> +#include <linux/errno.h> +#include <linux/mutex.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/msm_ba.h> + +#include "msm_ba_internal.h" +#include "msm_ba_debug.h" + +#define BASE_DEVICE_NUMBER 35 + +static struct ba_ctxt *gp_ba_ctxt; + +struct ba_ctxt *msm_ba_get_ba_context(void) +{ + return gp_ba_ctxt; +} + +static void msm_ba_set_ba_context(struct ba_ctxt *ba_ctxt) +{ + gp_ba_ctxt = ba_ctxt; +} + +static inline struct msm_ba_inst *get_ba_inst(struct file *filp, void *fh) +{ + return container_of(filp->private_data, + struct msm_ba_inst, event_handler); +} + +static int msm_ba_v4l2_open(struct file *filp) +{ + struct video_device *vdev = video_devdata(filp); + struct msm_ba_inst *ba_inst; + + ba_inst = msm_ba_open(NULL); + if (!ba_inst) { + dprintk(BA_ERR, + "Failed to create video instance"); + return -ENOMEM; + } + clear_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags); + filp->private_data = &(ba_inst->event_handler); + return 0; +} + +static int msm_ba_v4l2_close(struct file *filp) +{ + int rc = 0; + struct msm_ba_inst *ba_inst; + + ba_inst = get_ba_inst(filp, NULL); + + rc = msm_ba_close(ba_inst); + return rc; +} + +static int msm_ba_v4l2_querycap(struct file *filp, void *fh, + struct v4l2_capability *cap) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(filp, fh); + + return msm_ba_querycap((void *)ba_inst, cap); +} + +static int msm_ba_v4l2_enum_input(struct file *file, void *fh, + struct v4l2_input *input) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_enum_input((void *)ba_inst, input); +} + +static int msm_ba_v4l2_g_input(struct file *file, void *fh, + unsigned int *index) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_g_input((void *)ba_inst, index); +} + +static int msm_ba_v4l2_s_input(struct file *file, void *fh, + unsigned int index) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_s_input((void *)ba_inst, index); +} + +static int msm_ba_v4l2_enum_output(struct file *file, void *fh, + struct v4l2_output *output) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_enum_output((void *)ba_inst, output); +} + +static int msm_ba_v4l2_g_output(struct file *file, void *fh, + unsigned int *index) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_g_output((void *)ba_inst, index); +} + +static int msm_ba_v4l2_s_output(struct file *file, void *fh, + unsigned int index) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_s_output((void *)ba_inst, index); +} + +static int msm_ba_v4l2_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_enum_fmt((void *)ba_inst, f); +} + +static int msm_ba_v4l2_s_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_s_fmt((void *)ba_inst, f); +} + +static int msm_ba_v4l2_g_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_g_fmt((void *)ba_inst, f); +} + +static int msm_ba_v4l2_s_ctrl(struct file *file, void *fh, + struct v4l2_control *a) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_s_ctrl((void *)ba_inst, a); +} + +static int msm_ba_v4l2_g_ctrl(struct file *file, void *fh, + struct v4l2_control *a) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_g_ctrl((void *)ba_inst, a); +} + +static int msm_ba_v4l2_s_ext_ctrl(struct file *file, void *fh, + struct v4l2_ext_controls *a) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_s_ext_ctrl((void *)ba_inst, a); +} + +static int msm_ba_v4l2_streamon(struct file *file, void *fh, + enum v4l2_buf_type i) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_streamon((void *)ba_inst, i); +} + +static int msm_ba_v4l2_streamoff(struct file *file, void *fh, + enum v4l2_buf_type i) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_streamoff((void *)ba_inst, i); +} + +static int msm_ba_v4l2_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct msm_ba_inst *ba_inst = container_of(fh, + struct msm_ba_inst, event_handler); + + return msm_ba_subscribe_event((void *)ba_inst, sub); +} + +static int msm_ba_v4l2_unsubscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct msm_ba_inst *ba_inst = container_of(fh, + struct msm_ba_inst, event_handler); + + return msm_ba_unsubscribe_event((void *)ba_inst, sub); +} + +static int msm_ba_v4l2_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(file, fh); + + return msm_ba_s_parm((void *)ba_inst, a); +} + +static int msm_ba_v4l2_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + return 0; +} + +static const struct v4l2_ioctl_ops msm_ba_v4l2_ioctl_ops = { + .vidioc_querycap = msm_ba_v4l2_querycap, + .vidioc_enum_fmt_vid_cap = msm_ba_v4l2_enum_fmt, + .vidioc_enum_fmt_vid_out = msm_ba_v4l2_enum_fmt, + .vidioc_s_fmt_vid_cap = msm_ba_v4l2_s_fmt, + .vidioc_s_fmt_vid_cap_mplane = msm_ba_v4l2_s_fmt, + .vidioc_g_fmt_vid_cap = msm_ba_v4l2_g_fmt, + .vidioc_g_fmt_vid_cap_mplane = msm_ba_v4l2_g_fmt, + .vidioc_streamon = msm_ba_v4l2_streamon, + .vidioc_streamoff = msm_ba_v4l2_streamoff, + .vidioc_s_ctrl = msm_ba_v4l2_s_ctrl, + .vidioc_g_ctrl = msm_ba_v4l2_g_ctrl, + .vidioc_s_ext_ctrls = msm_ba_v4l2_s_ext_ctrl, + .vidioc_subscribe_event = msm_ba_v4l2_subscribe_event, + .vidioc_unsubscribe_event = msm_ba_v4l2_unsubscribe_event, + .vidioc_s_parm = msm_ba_v4l2_s_parm, + .vidioc_g_parm = msm_ba_v4l2_g_parm, + .vidioc_enum_input = msm_ba_v4l2_enum_input, + .vidioc_g_input = msm_ba_v4l2_g_input, + .vidioc_s_input = msm_ba_v4l2_s_input, + .vidioc_enum_output = msm_ba_v4l2_enum_output, + .vidioc_g_output = msm_ba_v4l2_g_output, + .vidioc_s_output = msm_ba_v4l2_s_output, +}; + +static unsigned int msm_ba_v4l2_poll(struct file *filp, + struct poll_table_struct *pt) +{ + struct msm_ba_inst *ba_inst = get_ba_inst(filp, NULL); + + return msm_ba_poll((void *)ba_inst, filp, pt); +} + +static void msm_ba_release_video_device(struct video_device *pvdev) +{ +} + +static const struct v4l2_file_operations msm_ba_v4l2_ba_fops = { + .owner = THIS_MODULE, + .open = msm_ba_v4l2_open, + .release = msm_ba_v4l2_close, + .unlocked_ioctl = video_ioctl2, + .poll = msm_ba_v4l2_poll, +}; + +static int parse_ba_dt(struct platform_device *pdev) +{ + uint32_t profile_count = 0; + struct device_node *np = pdev->dev.of_node; + struct device_node *child_np = NULL; + struct msm_ba_dev *dev_ctxt = NULL; + struct ba_ctxt *ba_ctxt = msm_ba_get_ba_context(); + char *key = NULL; + uint32_t err = 0, i = 0; + + dev_ctxt = ba_ctxt->dev_ctxt; + + profile_count = of_get_child_count(np); + if (profile_count == 0) { + dprintk(BA_ERR, "%s: Error reading DT. node=%s", + __func__, np->full_name); + return -ENODEV; + } + + dev_ctxt->msm_ba_inp_cfg = devm_kzalloc(&pdev->dev, + sizeof(struct msm_ba_input_config) * profile_count, + GFP_KERNEL); + if (!dev_ctxt->msm_ba_inp_cfg) + return -ENOMEM; + + i = 0; + for_each_child_of_node(np, child_np) { + key = "qcom,type"; + err = of_property_read_u32(child_np, key, + &dev_ctxt->msm_ba_inp_cfg[i].input_type); + if (err) + goto read_fail; + + key = "qcom,name"; + err = of_property_read_string(child_np, key, + &dev_ctxt->msm_ba_inp_cfg[i].name); + if (err) + goto read_fail; + + key = "qcom,ba-input"; + err = of_property_read_u32(child_np, key, + &dev_ctxt->msm_ba_inp_cfg[i].ba_ip); + if (err) + goto read_fail; + + key = "qcom,ba-output"; + err = of_property_read_u32(child_np, key, + &dev_ctxt->msm_ba_inp_cfg[i].ba_out); + if (err) + goto read_fail; + + key = "qcom,sd-name"; + err = of_property_read_string(child_np, key, + &dev_ctxt->msm_ba_inp_cfg[i].sd_name); + if (err) + goto read_fail; + + key = "qcom,ba-node"; + err = of_property_read_u32(child_np, key, + &dev_ctxt->msm_ba_inp_cfg[i].ba_node); + if (err) + goto read_fail; + + + key = "qcom,user-type"; + err = of_property_read_u32(child_np, key, + &dev_ctxt->msm_ba_inp_cfg[i].input_user_type); + if (err) + goto read_fail; + + i++; + } + dev_ctxt->num_config_inputs = i; + +read_fail: + if (err) { + dprintk(BA_INFO, "%s: Error reading DT. node=%s key=%s", + __func__, np->full_name, key); + devm_kfree(&pdev->dev, dev_ctxt->msm_ba_inp_cfg); + + dev_ctxt->num_config_inputs = 0; + } + + return err; +} + +static int msm_ba_device_init(struct platform_device *pdev, + struct msm_ba_dev **ret_dev_ctxt) +{ + struct msm_ba_dev *dev_ctxt; + int nr = BASE_DEVICE_NUMBER; + int rc = 0; + + dprintk(BA_INFO, "Enter %s", __func__); + if ((ret_dev_ctxt == NULL) || + (*ret_dev_ctxt != NULL) || + (pdev == NULL)) { + dprintk(BA_ERR, "%s(%d) Invalid params", + __func__, __LINE__); + return -EINVAL; + } + + dev_ctxt = devm_kzalloc(&pdev->dev, sizeof(struct msm_ba_dev), + GFP_KERNEL); + if (dev_ctxt == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, dev_ctxt); + + INIT_LIST_HEAD(&dev_ctxt->inputs); + INIT_LIST_HEAD(&dev_ctxt->instances); + INIT_LIST_HEAD(&dev_ctxt->sd_events); + INIT_DELAYED_WORK(&dev_ctxt->sd_events_work, + msm_ba_subdev_event_hndlr_delayed); + mutex_init(&dev_ctxt->dev_cs); + + dev_ctxt->state = BA_DEV_UNINIT; + + strlcpy(dev_ctxt->v4l2_dev.name, MSM_BA_DRV_NAME, + sizeof(dev_ctxt->v4l2_dev.name)); + dev_ctxt->v4l2_dev.dev = &pdev->dev; + dev_ctxt->v4l2_dev.notify = msm_ba_subdev_event_hndlr; + + rc = v4l2_device_register(dev_ctxt->v4l2_dev.dev, &dev_ctxt->v4l2_dev); + if (!rc) { + dev_ctxt->vdev = video_device_alloc(); + if (dev_ctxt->vdev == NULL) { + v4l2_device_unregister(&dev_ctxt->v4l2_dev); + rc = -ENOMEM; + } else { + strlcpy(dev_ctxt->vdev->name, + pdev->name, sizeof(dev_ctxt->vdev->name)); + dev_ctxt->vdev->v4l2_dev = &dev_ctxt->v4l2_dev; + dev_ctxt->vdev->release = msm_ba_release_video_device; + dev_ctxt->vdev->fops = &msm_ba_v4l2_ba_fops; + dev_ctxt->vdev->ioctl_ops = &msm_ba_v4l2_ioctl_ops; + dev_ctxt->vdev->minor = nr; + dev_ctxt->vdev->vfl_type = VFL_TYPE_GRABBER; + + video_set_drvdata(dev_ctxt->vdev, &dev_ctxt); + + strlcpy(dev_ctxt->mdev.model, MSM_BA_DRV_NAME, + sizeof(dev_ctxt->mdev.model)); + dev_ctxt->mdev.dev = &pdev->dev; + rc = media_device_register(&dev_ctxt->mdev); + dev_ctxt->v4l2_dev.mdev = &dev_ctxt->mdev; + rc = media_entity_init(&dev_ctxt->vdev->entity, + 0, NULL, 0); + dev_ctxt->vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L; + dev_ctxt->vdev->entity.group_id = 2; + + rc = video_register_device(dev_ctxt->vdev, + VFL_TYPE_GRABBER, nr); + if (!rc) { + dev_ctxt->vdev->entity.name = + video_device_node_name(dev_ctxt->vdev); + *ret_dev_ctxt = dev_ctxt; + } else { + dprintk(BA_ERR, + "Failed to register BA video device"); + } + } + } else { + dprintk(BA_ERR, "Failed to register v4l2 device"); + } + + if (rc) { + devm_kfree(&pdev->dev, dev_ctxt); + dev_ctxt = NULL; + } + dprintk(BA_INFO, "Exit %s with error %d", __func__, rc); + + return rc; +} + +static int msm_ba_probe(struct platform_device *pdev) +{ + struct ba_ctxt *ba_ctxt; + int rc = 0; + + dprintk(BA_INFO, "Enter %s: pdev %pK device id = %d", + __func__, pdev, pdev->id); + ba_ctxt = msm_ba_get_ba_context(); + + if (ba_ctxt == NULL) { + dprintk(BA_ERR, "BA context not yet created"); + return -EINVAL; + } + rc = msm_ba_device_init(pdev, &ba_ctxt->dev_ctxt); + if (rc) + dprintk(BA_ERR, "Failed to init device"); + else + ba_ctxt->dev_ctxt->debugfs_root = msm_ba_debugfs_init_dev( + ba_ctxt->dev_ctxt, ba_ctxt->debugfs_root); + + rc = parse_ba_dt(pdev); + if (rc < 0) { + dprintk(BA_ERR, "%s: devicetree error. Exit init", __func__); + return rc; + } + dprintk(BA_INFO, "Exit %s with error %d", __func__, rc); + + return rc; +} + +static int msm_ba_remove(struct platform_device *pdev) +{ + struct msm_ba_dev *dev_ctxt = platform_get_drvdata(pdev); + struct msm_ba_sd_event *ba_sd_event = NULL; + struct msm_ba_sd_event *ba_sd_event_tmp = NULL; + int rc = 0; + + if (dev_ctxt == NULL) { + dprintk(BA_ERR, "%s invalid device", __func__); + rc = -EINVAL; + } else { + video_unregister_device(dev_ctxt->vdev); + v4l2_device_unregister(&dev_ctxt->v4l2_dev); + cancel_delayed_work_sync(&dev_ctxt->sd_events_work); + list_for_each_entry_safe(ba_sd_event, ba_sd_event_tmp, + &dev_ctxt->sd_events, list) { + list_del(&ba_sd_event->list); + kfree(ba_sd_event); + } + + devm_kfree(&pdev->dev, dev_ctxt->msm_ba_inp_cfg); + devm_kfree(&pdev->dev, dev_ctxt); + dev_ctxt = NULL; + } + dprintk(BA_INFO, "Exit %s with error %d", __func__, rc); + + return rc; +} + +static int msm_ba_create(void) +{ + struct ba_ctxt *ba_ctxt; + int rc = 0; + + ba_ctxt = msm_ba_get_ba_context(); + + if (ba_ctxt != NULL) { + dprintk(BA_ERR, "BA context already created"); + return -EINVAL; + } + ba_ctxt = kzalloc(sizeof(struct ba_ctxt), GFP_KERNEL); + + if (ba_ctxt == NULL) + return -ENOMEM; + + memset(ba_ctxt, 0x00, sizeof(struct ba_ctxt)); + + mutex_init(&ba_ctxt->ba_cs); + ba_ctxt->debugfs_root = msm_ba_debugfs_init_drv(); + if (!ba_ctxt->debugfs_root) + dprintk(BA_ERR, + "Failed to create debugfs for msm_ba"); + + msm_ba_set_ba_context(ba_ctxt); + + dprintk(BA_DBG, "%s(%d), BA create complete", + __func__, __LINE__); + + return rc; +} + +static int msm_ba_destroy(void) +{ + struct ba_ctxt *ba_ctxt; + int rc = 0; + + ba_ctxt = msm_ba_get_ba_context(); + + if (ba_ctxt == NULL) { + dprintk(BA_ERR, "BA context non existent"); + return -EINVAL; + } + + if (ba_ctxt->dev_ctxt != NULL) { + dprintk(BA_ERR, "Device instances exist on BA context"); + return -EBUSY; + } + mutex_destroy(&ba_ctxt->ba_cs); + + kfree(ba_ctxt); + ba_ctxt = NULL; + msm_ba_set_ba_context(ba_ctxt); + + return rc; +} + +static const struct of_device_id msm_ba_dt_match[] = { + {.compatible = "qcom,msm-ba"}, + {} +}; + +MODULE_DEVICE_TABLE(of, msm_ba_dt_match); + +static struct platform_driver msm_ba_driver = { + .probe = msm_ba_probe, + .remove = msm_ba_remove, + .driver = { + .name = "msm_ba_v4l2", + .owner = THIS_MODULE, + .of_match_table = msm_ba_dt_match, + }, +}; + +static int __init msm_ba_mod_init(void) +{ + int rc = 0; + + dprintk(BA_INFO, "Enter %s", __func__); + rc = msm_ba_create(); + if (!rc) { + rc = platform_driver_register(&msm_ba_driver); + if (rc) { + dprintk(BA_ERR, + "Failed to register platform driver"); + msm_ba_destroy(); + } + } + dprintk(BA_INFO, "Exit %s with error %d", __func__, rc); + + return rc; +} + +static void __exit msm_ba_mod_exit(void) +{ + int rc = 0; + + dprintk(BA_INFO, "Enter %s", __func__); + platform_driver_unregister(&msm_ba_driver); + rc = msm_ba_destroy(); + dprintk(BA_INFO, "Exit %s", __func__); +} + +module_init(msm_ba_mod_init); +module_exit(msm_ba_mod_exit); + diff --git a/drivers/watchdog/pcwd_usb.c b/drivers/watchdog/pcwd_usb.c index 1a11aedc4fe8..9eb5b314ba06 100644 --- a/drivers/watchdog/pcwd_usb.c +++ b/drivers/watchdog/pcwd_usb.c @@ -630,6 +630,9 @@ static int usb_pcwd_probe(struct usb_interface *interface, return -ENODEV; } + if (iface_desc->desc.bNumEndpoints < 1) + return -ENODEV; + /* check out the endpoint: it has to be Interrupt & IN */ endpoint = &iface_desc->endpoint[0].desc; diff --git a/drivers/xen/privcmd.c b/drivers/xen/privcmd.c index df2e6f783318..527de56f832f 100644 --- a/drivers/xen/privcmd.c +++ b/drivers/xen/privcmd.c @@ -335,8 +335,8 @@ static int mmap_batch_fn(void *data, int nr, void *state) st->global_error = 1; } } - st->va += PAGE_SIZE * nr; - st->index += nr; + st->va += XEN_PAGE_SIZE * nr; + st->index += nr / XEN_PFN_PER_PAGE; return 0; } |
