diff options
Diffstat (limited to 'drivers')
303 files changed, 5815 insertions, 3076 deletions
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index eaa60c94205a..1f32caa87686 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -30,6 +30,7 @@ static const struct acpi_device_id forbidden_id_list[] = { {"PNP0200", 0}, /* AT DMA Controller */ {"ACPI0009", 0}, /* IOxAPIC */ {"ACPI000A", 0}, /* IOAPIC */ + {"SMB0001", 0}, /* ACPI SMBUS virtual device */ {"", 0}, }; diff --git a/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c index 3f0e2a14895a..22b53bf26817 100644 --- a/drivers/cpufreq/ti-cpufreq.c +++ b/drivers/cpufreq/ti-cpufreq.c @@ -201,19 +201,28 @@ static const struct of_device_id ti_cpufreq_of_match[] = { {}, }; +static const struct of_device_id *ti_cpufreq_match_node(void) +{ + struct device_node *np; + const struct of_device_id *match; + + np = of_find_node_by_path("/"); + match = of_match_node(ti_cpufreq_of_match, np); + of_node_put(np); + + return match; +} + static int ti_cpufreq_probe(struct platform_device *pdev) { u32 version[VERSION_COUNT]; - struct device_node *np; const struct of_device_id *match; struct opp_table *ti_opp_table; struct ti_cpufreq_data *opp_data; const char * const reg_names[] = {"vdd", "vbb"}; int ret; - np = of_find_node_by_path("/"); - match = of_match_node(ti_cpufreq_of_match, np); - of_node_put(np); + match = dev_get_platdata(&pdev->dev); if (!match) return -ENODEV; @@ -290,7 +299,14 @@ fail_put_node: static int ti_cpufreq_init(void) { - platform_device_register_simple("ti-cpufreq", -1, NULL, 0); + const struct of_device_id *match; + + /* Check to ensure we are on a compatible platform */ + match = ti_cpufreq_match_node(); + if (match) + platform_device_register_data(NULL, "ti-cpufreq", -1, match, + sizeof(*match)); + return 0; } module_init(ti_cpufreq_init); diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 02f7f9a89979..7c858020d14b 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -1093,17 +1093,7 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused) return 0; } -static int dma_buf_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, dma_buf_debug_show, NULL); -} - -static const struct file_operations dma_buf_debug_fops = { - .open = dma_buf_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(dma_buf_debug); static struct dentry *dma_buf_debugfs_dir; diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index 1551ca7df394..3aa8733f832a 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -30,13 +30,16 @@ EXPORT_TRACEPOINT_SYMBOL(dma_fence_emit); EXPORT_TRACEPOINT_SYMBOL(dma_fence_enable_signal); +static DEFINE_SPINLOCK(dma_fence_stub_lock); +static struct dma_fence dma_fence_stub; + /* * fence context counter: each execution context should have its own * fence context, this allows checking if fences belong to the same * context or not. One device can have multiple separate contexts, * and they're used if some engine can run independently of another. */ -static atomic64_t dma_fence_context_counter = ATOMIC64_INIT(0); +static atomic64_t dma_fence_context_counter = ATOMIC64_INIT(1); /** * DOC: DMA fences overview @@ -68,6 +71,37 @@ static atomic64_t dma_fence_context_counter = ATOMIC64_INIT(0); * &dma_buf.resv pointer. */ +static const char *dma_fence_stub_get_name(struct dma_fence *fence) +{ + return "stub"; +} + +static const struct dma_fence_ops dma_fence_stub_ops = { + .get_driver_name = dma_fence_stub_get_name, + .get_timeline_name = dma_fence_stub_get_name, +}; + +/** + * dma_fence_get_stub - return a signaled fence + * + * Return a stub fence which is already signaled. + */ +struct dma_fence *dma_fence_get_stub(void) +{ + spin_lock(&dma_fence_stub_lock); + if (!dma_fence_stub.ops) { + dma_fence_init(&dma_fence_stub, + &dma_fence_stub_ops, + &dma_fence_stub_lock, + 0, 0); + dma_fence_signal_locked(&dma_fence_stub); + } + spin_unlock(&dma_fence_stub_lock); + + return dma_fence_get(&dma_fence_stub); +} +EXPORT_SYMBOL(dma_fence_get_stub); + /** * dma_fence_context_alloc - allocate an array of fence contexts * @num: amount of contexts to allocate @@ -615,7 +649,7 @@ EXPORT_SYMBOL(dma_fence_wait_any_timeout); */ void dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops, - spinlock_t *lock, u64 context, unsigned seqno) + spinlock_t *lock, u64 context, u64 seqno) { BUG_ON(!lock); BUG_ON(!ops || !ops->get_driver_name || !ops->get_timeline_name); diff --git a/drivers/dma-buf/sw_sync.c b/drivers/dma-buf/sw_sync.c index 53c1d6d36a64..32dcf7b4c935 100644 --- a/drivers/dma-buf/sw_sync.c +++ b/drivers/dma-buf/sw_sync.c @@ -172,7 +172,7 @@ static bool timeline_fence_enable_signaling(struct dma_fence *fence) static void timeline_fence_value_str(struct dma_fence *fence, char *str, int size) { - snprintf(str, size, "%d", fence->seqno); + snprintf(str, size, "%lld", fence->seqno); } static void timeline_fence_timeline_value_str(struct dma_fence *fence, diff --git a/drivers/dma-buf/sync_debug.c b/drivers/dma-buf/sync_debug.c index c4c8ecb24aa9..c0abf37df88b 100644 --- a/drivers/dma-buf/sync_debug.c +++ b/drivers/dma-buf/sync_debug.c @@ -147,7 +147,7 @@ static void sync_print_sync_file(struct seq_file *s, } } -static int sync_debugfs_show(struct seq_file *s, void *unused) +static int sync_info_debugfs_show(struct seq_file *s, void *unused) { struct list_head *pos; @@ -178,17 +178,7 @@ static int sync_debugfs_show(struct seq_file *s, void *unused) return 0; } -static int sync_info_debugfs_open(struct inode *inode, struct file *file) -{ - return single_open(file, sync_debugfs_show, inode->i_private); -} - -static const struct file_operations sync_info_debugfs_fops = { - .open = sync_info_debugfs_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(sync_info_debugfs); static __init int sync_debugfs_init(void) { @@ -218,7 +208,7 @@ void sync_dump(void) }; int i; - sync_debugfs_show(&s, NULL); + sync_info_debugfs_show(&s, NULL); for (i = 0; i < s.count; i += DUMP_CHUNK) { if ((s.count - i) > DUMP_CHUNK) { diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c index 35dd06479867..4f6305ca52c8 100644 --- a/drivers/dma-buf/sync_file.c +++ b/drivers/dma-buf/sync_file.c @@ -144,7 +144,7 @@ char *sync_file_get_name(struct sync_file *sync_file, char *buf, int len) } else { struct dma_fence *fence = sync_file->fence; - snprintf(buf, len, "%s-%s%llu-%d", + snprintf(buf, len, "%s-%s%llu-%lld", fence->ops->get_driver_name(fence), fence->ops->get_timeline_name(fence), fence->context, @@ -258,7 +258,7 @@ static struct sync_file *sync_file_merge(const char *name, struct sync_file *a, i_b++; } else { - if (pt_a->seqno - pt_b->seqno <= INT_MAX) + if (__dma_fence_is_later(pt_a->seqno, pt_b->seqno)) add_fence(fences, &i, pt_a); else add_fence(fences, &i, pt_b); diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c index 5b44ef226904..fc359ca4503d 100644 --- a/drivers/dma-buf/udmabuf.c +++ b/drivers/dma-buf/udmabuf.c @@ -184,6 +184,7 @@ static long udmabuf_create(const struct udmabuf_create_list *head, exp_info.ops = &udmabuf_ops; exp_info.size = ubuf->pagecount << PAGE_SHIFT; exp_info.priv = ubuf; + exp_info.flags = O_RDWR; buf = dma_buf_export(&exp_info); if (IS_ERR(buf)) { diff --git a/drivers/gnss/serial.c b/drivers/gnss/serial.c index b01ba4438501..31e891f00175 100644 --- a/drivers/gnss/serial.c +++ b/drivers/gnss/serial.c @@ -13,6 +13,7 @@ #include <linux/of.h> #include <linux/pm.h> #include <linux/pm_runtime.h> +#include <linux/sched.h> #include <linux/serdev.h> #include <linux/slab.h> @@ -63,7 +64,7 @@ static int gnss_serial_write_raw(struct gnss_device *gdev, int ret; /* write is only buffered synchronously */ - ret = serdev_device_write(serdev, buf, count, 0); + ret = serdev_device_write(serdev, buf, count, MAX_SCHEDULE_TIMEOUT); if (ret < 0) return ret; diff --git a/drivers/gnss/sirf.c b/drivers/gnss/sirf.c index 79cb98950013..71d014edd167 100644 --- a/drivers/gnss/sirf.c +++ b/drivers/gnss/sirf.c @@ -16,6 +16,7 @@ #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/sched.h> #include <linux/serdev.h> #include <linux/slab.h> #include <linux/wait.h> @@ -83,7 +84,7 @@ static int sirf_write_raw(struct gnss_device *gdev, const unsigned char *buf, int ret; /* write is only buffered synchronously */ - ret = serdev_device_write(serdev, buf, count, 0); + ret = serdev_device_write(serdev, buf, count, MAX_SCHEDULE_TIMEOUT); if (ret < 0) return ret; diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 8269cffc2967..6a50f9f59c90 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -35,8 +35,8 @@ #define gpio_mockup_err(...) pr_err(GPIO_MOCKUP_NAME ": " __VA_ARGS__) enum { - GPIO_MOCKUP_DIR_OUT = 0, - GPIO_MOCKUP_DIR_IN = 1, + GPIO_MOCKUP_DIR_IN = 0, + GPIO_MOCKUP_DIR_OUT = 1, }; /* @@ -131,7 +131,7 @@ static int gpio_mockup_get_direction(struct gpio_chip *gc, unsigned int offset) { struct gpio_mockup_chip *chip = gpiochip_get_data(gc); - return chip->lines[offset].dir; + return !chip->lines[offset].dir; } static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset) diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index bfe4c5c9f41c..e9600b556f39 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -268,8 +268,8 @@ static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset) if (pxa_gpio_has_pinctrl()) { ret = pinctrl_gpio_direction_input(chip->base + offset); - if (!ret) - return 0; + if (ret) + return ret; } spin_lock_irqsave(&gpio_lock, flags); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 230e41562462..a2cbb474901c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1295,7 +1295,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL); if (!gdev->descs) { status = -ENOMEM; - goto err_free_gdev; + goto err_free_ida; } if (chip->ngpio == 0) { @@ -1427,8 +1427,9 @@ err_free_label: kfree_const(gdev->label); err_free_descs: kfree(gdev->descs); -err_free_gdev: +err_free_ida: ida_simple_remove(&gpio_ida, gdev->id); +err_free_gdev: /* failures here can mean systems won't boot... */ pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__, gdev->base, gdev->base + gdev->ngpio - 1, diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 7f3be3506057..1fafc2f8e8f9 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -10,7 +10,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_scatter.o drm_pci.o \ drm_sysfs.o drm_hashtab.o drm_mm.o \ drm_crtc.o drm_fourcc.o drm_modes.o drm_edid.o \ - drm_info.o drm_encoder_slave.o \ + drm_encoder_slave.o \ drm_trace_points.o drm_prime.o \ drm_rect.o drm_vma_manager.o drm_flip_work.o \ drm_modeset_lock.o drm_atomic.o drm_bridge.o \ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index 60f9a87e9c74..bcf1666fb31d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -501,8 +501,11 @@ void amdgpu_amdkfd_set_compute_idle(struct kgd_dev *kgd, bool idle) { struct amdgpu_device *adev = (struct amdgpu_device *)kgd; - amdgpu_dpm_switch_power_profile(adev, - PP_SMC_POWER_PROFILE_COMPUTE, !idle); + if (adev->powerplay.pp_funcs && + adev->powerplay.pp_funcs->switch_power_profile) + amdgpu_dpm_switch_power_profile(adev, + PP_SMC_POWER_PROFILE_COMPUTE, + !idle); } bool amdgpu_amdkfd_is_kfd_vmid(struct amdgpu_device *adev, u32 vmid) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 024dfbd87f11..dc54e9efd910 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -1193,7 +1193,7 @@ static void amdgpu_cs_post_dependencies(struct amdgpu_cs_parser *p) int i; for (i = 0; i < p->num_post_dep_syncobjs; ++i) - drm_syncobj_replace_fence(p->post_dep_syncobjs[i], 0, p->fence); + drm_syncobj_replace_fence(p->post_dep_syncobjs[i], p->fence); } static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c index 6748cd7fc129..686a26de50f9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c @@ -626,6 +626,13 @@ int amdgpu_display_modeset_create_props(struct amdgpu_device *adev) "dither", amdgpu_dither_enum_list, sz); + if (amdgpu_device_has_dc_support(adev)) { + adev->mode_info.max_bpc_property = + drm_property_create_range(adev->ddev, 0, "max bpc", 8, 16); + if (!adev->mode_info.max_bpc_property) + return -ENOMEM; + } + return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h index 11723d8fffbd..0dc2c5c57015 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h @@ -338,6 +338,8 @@ struct amdgpu_mode_info { struct drm_property *audio_property; /* FMT dithering */ struct drm_property *dither_property; + /* maximum number of bits per channel for monitor color */ + struct drm_property *max_bpc_property; /* hardcoded DFP edid from BIOS */ struct edid *bios_hardcoded_edid; int bios_hardcoded_edid_size; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c index 12f2bf97611f..bfaf5c6323be 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c @@ -388,7 +388,7 @@ void amdgpu_sa_bo_dump_debug_info(struct amdgpu_sa_manager *sa_manager, soffset, eoffset, eoffset - soffset); if (i->fence) - seq_printf(m, " protected by 0x%08x on context %llu", + seq_printf(m, " protected by 0x%016llx on context %llu", i->fence->seqno, i->fence->context); seq_printf(m, "\n"); diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c index 2821d1d846e4..9fc3296592fe 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c @@ -46,6 +46,7 @@ MODULE_FIRMWARE("amdgpu/tahiti_mc.bin"); MODULE_FIRMWARE("amdgpu/pitcairn_mc.bin"); MODULE_FIRMWARE("amdgpu/verde_mc.bin"); MODULE_FIRMWARE("amdgpu/oland_mc.bin"); +MODULE_FIRMWARE("amdgpu/hainan_mc.bin"); MODULE_FIRMWARE("amdgpu/si58_mc.bin"); #define MC_SEQ_MISC0__MT__MASK 0xf0000000 diff --git a/drivers/gpu/drm/amd/amdgpu/soc15.c b/drivers/gpu/drm/amd/amdgpu/soc15.c index bf5e6a413dee..4cc0dcb1a187 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc15.c +++ b/drivers/gpu/drm/amd/amdgpu/soc15.c @@ -65,6 +65,13 @@ #define mmMP0_MISC_LIGHT_SLEEP_CTRL 0x01ba #define mmMP0_MISC_LIGHT_SLEEP_CTRL_BASE_IDX 0 +/* for Vega20 register name change */ +#define mmHDP_MEM_POWER_CTRL 0x00d4 +#define HDP_MEM_POWER_CTRL__IPH_MEM_POWER_CTRL_EN_MASK 0x00000001L +#define HDP_MEM_POWER_CTRL__IPH_MEM_POWER_LS_EN_MASK 0x00000002L +#define HDP_MEM_POWER_CTRL__RC_MEM_POWER_CTRL_EN_MASK 0x00010000L +#define HDP_MEM_POWER_CTRL__RC_MEM_POWER_LS_EN_MASK 0x00020000L +#define mmHDP_MEM_POWER_CTRL_BASE_IDX 0 /* * Indirect registers accessor */ @@ -870,15 +877,33 @@ static void soc15_update_hdp_light_sleep(struct amdgpu_device *adev, bool enable { uint32_t def, data; - def = data = RREG32(SOC15_REG_OFFSET(HDP, 0, mmHDP_MEM_POWER_LS)); + if (adev->asic_type == CHIP_VEGA20) { + def = data = RREG32(SOC15_REG_OFFSET(HDP, 0, mmHDP_MEM_POWER_CTRL)); - if (enable && (adev->cg_flags & AMD_CG_SUPPORT_HDP_LS)) - data |= HDP_MEM_POWER_LS__LS_ENABLE_MASK; - else - data &= ~HDP_MEM_POWER_LS__LS_ENABLE_MASK; + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_HDP_LS)) + data |= HDP_MEM_POWER_CTRL__IPH_MEM_POWER_CTRL_EN_MASK | + HDP_MEM_POWER_CTRL__IPH_MEM_POWER_LS_EN_MASK | + HDP_MEM_POWER_CTRL__RC_MEM_POWER_CTRL_EN_MASK | + HDP_MEM_POWER_CTRL__RC_MEM_POWER_LS_EN_MASK; + else + data &= ~(HDP_MEM_POWER_CTRL__IPH_MEM_POWER_CTRL_EN_MASK | + HDP_MEM_POWER_CTRL__IPH_MEM_POWER_LS_EN_MASK | + HDP_MEM_POWER_CTRL__RC_MEM_POWER_CTRL_EN_MASK | + HDP_MEM_POWER_CTRL__RC_MEM_POWER_LS_EN_MASK); - if (def != data) - WREG32(SOC15_REG_OFFSET(HDP, 0, mmHDP_MEM_POWER_LS), data); + if (def != data) + WREG32(SOC15_REG_OFFSET(HDP, 0, mmHDP_MEM_POWER_CTRL), data); + } else { + def = data = RREG32(SOC15_REG_OFFSET(HDP, 0, mmHDP_MEM_POWER_LS)); + + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_HDP_LS)) + data |= HDP_MEM_POWER_LS__LS_ENABLE_MASK; + else + data &= ~HDP_MEM_POWER_LS__LS_ENABLE_MASK; + + if (def != data) + WREG32(SOC15_REG_OFFSET(HDP, 0, mmHDP_MEM_POWER_LS), data); + } } static void soc15_update_drm_clock_gating(struct amdgpu_device *adev, bool enable) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index aa43bb253ea2..d8d0b206a79c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -2422,8 +2422,15 @@ static void update_stream_scaling_settings(const struct drm_display_mode *mode, static enum dc_color_depth convert_color_depth_from_display_info(const struct drm_connector *connector) { + struct dm_connector_state *dm_conn_state = + to_dm_connector_state(connector->state); uint32_t bpc = connector->display_info.bpc; + /* TODO: Remove this when there's support for max_bpc in drm */ + if (dm_conn_state && bpc > dm_conn_state->max_bpc) + /* Round down to nearest even number. */ + bpc = dm_conn_state->max_bpc - (dm_conn_state->max_bpc & 1); + switch (bpc) { case 0: /* @@ -3007,6 +3014,9 @@ int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector, } else if (property == adev->mode_info.underscan_property) { dm_new_state->underscan_enable = val; ret = 0; + } else if (property == adev->mode_info.max_bpc_property) { + dm_new_state->max_bpc = val; + ret = 0; } return ret; @@ -3049,6 +3059,9 @@ int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector, } else if (property == adev->mode_info.underscan_property) { *val = dm_state->underscan_enable; ret = 0; + } else if (property == adev->mode_info.max_bpc_property) { + *val = dm_state->max_bpc; + ret = 0; } return ret; } @@ -3859,6 +3872,9 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, drm_object_attach_property(&aconnector->base.base, adev->mode_info.underscan_vborder_property, 0); + drm_object_attach_property(&aconnector->base.base, + adev->mode_info.max_bpc_property, + 0); } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index d6960644d714..607c3cdd7d0c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -252,6 +252,7 @@ struct dm_connector_state { enum amdgpu_rmx_type scaling; uint8_t underscan_vborder; uint8_t underscan_hborder; + uint8_t max_bpc; bool underscan_enable; bool freesync_enable; bool freesync_capable; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index d02c32a1039c..9fdeca096407 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -396,14 +396,6 @@ static void dm_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr, drm_connector_put(connector); } -static void dm_dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr) -{ - struct amdgpu_dm_connector *master = container_of(mgr, struct amdgpu_dm_connector, mst_mgr); - struct drm_device *dev = master->base.dev; - - drm_kms_helper_hotplug_event(dev); -} - static void dm_dp_mst_register_connector(struct drm_connector *connector) { struct drm_device *dev = connector->dev; @@ -420,7 +412,6 @@ static void dm_dp_mst_register_connector(struct drm_connector *connector) static const struct drm_dp_mst_topology_cbs dm_mst_cbs = { .add_connector = dm_dp_add_mst_connector, .destroy_connector = dm_dp_destroy_mst_connector, - .hotplug = dm_dp_mst_hotplug, .register_connector = dm_dp_mst_register_connector }; diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c index ed35ec0341e6..88f6b35ea6fe 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c @@ -4525,12 +4525,12 @@ static int smu7_get_sclk_od(struct pp_hwmgr *hwmgr) struct smu7_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table); struct smu7_single_dpm_table *golden_sclk_table = &(data->golden_dpm_table.sclk_table); - int value; + int value = sclk_table->dpm_levels[sclk_table->count - 1].value; + int golden_value = golden_sclk_table->dpm_levels + [golden_sclk_table->count - 1].value; - value = (sclk_table->dpm_levels[sclk_table->count - 1].value - - golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value) * - 100 / - golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value; + value -= golden_value; + value = DIV_ROUND_UP(value * 100, golden_value); return value; } @@ -4567,12 +4567,12 @@ static int smu7_get_mclk_od(struct pp_hwmgr *hwmgr) struct smu7_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table); struct smu7_single_dpm_table *golden_mclk_table = &(data->golden_dpm_table.mclk_table); - int value; + int value = mclk_table->dpm_levels[mclk_table->count - 1].value; + int golden_value = golden_mclk_table->dpm_levels + [golden_mclk_table->count - 1].value; - value = (mclk_table->dpm_levels[mclk_table->count - 1].value - - golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value) * - 100 / - golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value; + value -= golden_value; + value = DIV_ROUND_UP(value * 100, golden_value); return value; } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c index 8c4db86bb4b7..e2bc6e0c229f 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c @@ -4522,15 +4522,13 @@ static int vega10_get_sclk_od(struct pp_hwmgr *hwmgr) struct vega10_single_dpm_table *sclk_table = &(data->dpm_table.gfx_table); struct vega10_single_dpm_table *golden_sclk_table = &(data->golden_dpm_table.gfx_table); - int value; - - value = (sclk_table->dpm_levels[sclk_table->count - 1].value - - golden_sclk_table->dpm_levels - [golden_sclk_table->count - 1].value) * - 100 / - golden_sclk_table->dpm_levels + int value = sclk_table->dpm_levels[sclk_table->count - 1].value; + int golden_value = golden_sclk_table->dpm_levels [golden_sclk_table->count - 1].value; + value -= golden_value; + value = DIV_ROUND_UP(value * 100, golden_value); + return value; } @@ -4575,16 +4573,13 @@ static int vega10_get_mclk_od(struct pp_hwmgr *hwmgr) struct vega10_single_dpm_table *mclk_table = &(data->dpm_table.mem_table); struct vega10_single_dpm_table *golden_mclk_table = &(data->golden_dpm_table.mem_table); - int value; - - value = (mclk_table->dpm_levels - [mclk_table->count - 1].value - - golden_mclk_table->dpm_levels - [golden_mclk_table->count - 1].value) * - 100 / - golden_mclk_table->dpm_levels + int value = mclk_table->dpm_levels[mclk_table->count - 1].value; + int golden_value = golden_mclk_table->dpm_levels [golden_mclk_table->count - 1].value; + value -= golden_value; + value = DIV_ROUND_UP(value * 100, golden_value); + return value; } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c index 74bc37308dc0..54364444ecd1 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega12_hwmgr.c @@ -2243,12 +2243,12 @@ static int vega12_get_sclk_od(struct pp_hwmgr *hwmgr) struct vega12_single_dpm_table *sclk_table = &(data->dpm_table.gfx_table); struct vega12_single_dpm_table *golden_sclk_table = &(data->golden_dpm_table.gfx_table); - int value; + int value = sclk_table->dpm_levels[sclk_table->count - 1].value; + int golden_value = golden_sclk_table->dpm_levels + [golden_sclk_table->count - 1].value; - value = (sclk_table->dpm_levels[sclk_table->count - 1].value - - golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value) * - 100 / - golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value; + value -= golden_value; + value = DIV_ROUND_UP(value * 100, golden_value); return value; } @@ -2264,16 +2264,13 @@ static int vega12_get_mclk_od(struct pp_hwmgr *hwmgr) struct vega12_single_dpm_table *mclk_table = &(data->dpm_table.mem_table); struct vega12_single_dpm_table *golden_mclk_table = &(data->golden_dpm_table.mem_table); - int value; - - value = (mclk_table->dpm_levels - [mclk_table->count - 1].value - - golden_mclk_table->dpm_levels - [golden_mclk_table->count - 1].value) * - 100 / - golden_mclk_table->dpm_levels + int value = mclk_table->dpm_levels[mclk_table->count - 1].value; + int golden_value = golden_mclk_table->dpm_levels [golden_mclk_table->count - 1].value; + value -= golden_value; + value = DIV_ROUND_UP(value * 100, golden_value); + return value; } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c index f2daf00cc911..2679d1240fa1 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega20_hwmgr.c @@ -75,7 +75,17 @@ static void vega20_set_default_registry_data(struct pp_hwmgr *hwmgr) data->phy_clk_quad_eqn_b = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT; data->phy_clk_quad_eqn_c = PPREGKEY_VEGA20QUADRATICEQUATION_DFLT; - data->registry_data.disallowed_features = 0x0; + /* + * Disable the following features for now: + * GFXCLK DS + * SOCLK DS + * LCLK DS + * DCEFCLK DS + * FCLK DS + * MP1CLK DS + * MP0CLK DS + */ + data->registry_data.disallowed_features = 0xE0041C00; data->registry_data.od_state_in_dc_support = 0; data->registry_data.thermal_support = 1; data->registry_data.skip_baco_hardware = 0; @@ -1313,12 +1323,13 @@ static int vega20_get_sclk_od( &(data->dpm_table.gfx_table); struct vega20_single_dpm_table *golden_sclk_table = &(data->golden_dpm_table.gfx_table); - int value; + int value = sclk_table->dpm_levels[sclk_table->count - 1].value; + int golden_value = golden_sclk_table->dpm_levels + [golden_sclk_table->count - 1].value; /* od percentage */ - value = DIV_ROUND_UP((sclk_table->dpm_levels[sclk_table->count - 1].value - - golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value) * 100, - golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value); + value -= golden_value; + value = DIV_ROUND_UP(value * 100, golden_value); return value; } @@ -1358,12 +1369,13 @@ static int vega20_get_mclk_od( &(data->dpm_table.mem_table); struct vega20_single_dpm_table *golden_mclk_table = &(data->golden_dpm_table.mem_table); - int value; + int value = mclk_table->dpm_levels[mclk_table->count - 1].value; + int golden_value = golden_mclk_table->dpm_levels + [golden_mclk_table->count - 1].value; /* od percentage */ - value = DIV_ROUND_UP((mclk_table->dpm_levels[mclk_table->count - 1].value - - golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value) * 100, - golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value); + value -= golden_value; + value = DIV_ROUND_UP(value * 100, golden_value); return value; } diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c index 2af847ebca34..206a76abf771 100644 --- a/drivers/gpu/drm/arc/arcpgu_drv.c +++ b/drivers/gpu/drm/arc/arcpgu_drv.c @@ -190,7 +190,7 @@ err_unload: arcpgu_unload(drm); err_unref: - drm_dev_unref(drm); + drm_dev_put(drm); return ret; } @@ -201,7 +201,7 @@ static int arcpgu_remove(struct platform_device *pdev) drm_dev_unregister(drm); arcpgu_unload(drm); - drm_dev_unref(drm); + drm_dev_put(drm); return 0; } diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 69dab82a3771..bf589c53b908 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -60,8 +60,29 @@ static const struct pci_device_id pciidlist[] = { MODULE_DEVICE_TABLE(pci, pciidlist); +static void ast_kick_out_firmware_fb(struct pci_dev *pdev) +{ + struct apertures_struct *ap; + bool primary = false; + + ap = alloc_apertures(1); + if (!ap) + return; + + ap->ranges[0].base = pci_resource_start(pdev, 0); + ap->ranges[0].size = pci_resource_len(pdev, 0); + +#ifdef CONFIG_X86 + primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; +#endif + drm_fb_helper_remove_conflicting_framebuffers(ap, "astdrmfb", primary); + kfree(ap); +} + static int ast_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + ast_kick_out_firmware_fb(pdev); + return drm_get_pci_dev(pdev, ent, &driver); } diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index 0cd827e11fa2..a80bca1a857f 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -191,7 +191,6 @@ static int astfb_create(struct drm_fb_helper *helper, int size, ret; void *sysram; struct drm_gem_object *gobj = NULL; - struct ast_bo *bo = NULL; mode_cmd.width = sizes->surface_width; mode_cmd.height = sizes->surface_height; mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7)/8); @@ -206,7 +205,6 @@ static int astfb_create(struct drm_fb_helper *helper, DRM_ERROR("failed to create fbcon backing object %d\n", ret); return ret; } - bo = gem_to_ast_bo(gobj); sysram = vmalloc(size); if (!sysram) diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 5e77d456d9bb..7c6ac3cadb6b 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -568,6 +568,7 @@ static int ast_crtc_do_set_base(struct drm_crtc *crtc, } ast_bo_unreserve(bo); + ast_set_offset_reg(crtc); ast_set_start_address_crt1(crtc, (u32)gpu_addr); return 0; @@ -1254,7 +1255,7 @@ static int ast_cursor_move(struct drm_crtc *crtc, ast_set_index_reg(ast, AST_IO_CRTC_PORT, 0xc7, ((y >> 8) & 0x07)); /* dummy write to fire HWC */ - ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xCB, 0xFF, 0x00); + ast_show_cursor(crtc); return 0; } diff --git a/drivers/gpu/drm/ati_pcigart.c b/drivers/gpu/drm/ati_pcigart.c index 6c4d4b6eba80..2362f07fe1fc 100644 --- a/drivers/gpu/drm/ati_pcigart.c +++ b/drivers/gpu/drm/ati_pcigart.c @@ -103,7 +103,7 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga unsigned long pages; u32 *pci_gart = NULL, page_base, gart_idx; dma_addr_t bus_address = 0; - int i, j, ret = 0; + int i, j, ret = -ENOMEM; int max_ati_pages, max_real_pages; if (!entry) { @@ -117,7 +117,7 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga if (pci_set_dma_mask(dev->pdev, gart_info->table_mask)) { DRM_ERROR("fail to set dma mask to 0x%Lx\n", (unsigned long long)gart_info->table_mask); - ret = 1; + ret = -EFAULT; goto done; } @@ -160,6 +160,7 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga drm_ati_pcigart_cleanup(dev, gart_info); address = NULL; bus_address = 0; + ret = -ENOMEM; goto done; } page_base = (u32) entry->busaddr[i]; @@ -188,7 +189,7 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga page_base += ATI_PCIGART_PAGE_SIZE; } } - ret = 1; + ret = 0; #if defined(__i386__) || defined(__x86_64__) wbinvd(); diff --git a/drivers/gpu/drm/bochs/bochs_hw.c b/drivers/gpu/drm/bochs/bochs_hw.c index c90a0d492fd5..d0b4e1cee83e 100644 --- a/drivers/gpu/drm/bochs/bochs_hw.c +++ b/drivers/gpu/drm/bochs/bochs_hw.c @@ -86,9 +86,16 @@ static int bochs_get_edid_block(void *data, u8 *buf, int bochs_hw_load_edid(struct bochs_device *bochs) { + u8 header[8]; + if (!bochs->mmio) return -1; + /* check header to detect whenever edid support is enabled in qemu */ + bochs_get_edid_block(bochs, header, 0, ARRAY_SIZE(header)); + if (drm_edid_header_is_valid(header) != 8) + return -1; + kfree(bochs->edid); bochs->edid = drm_do_get_edid(&bochs->connector, bochs_get_edid_block, bochs); diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 9eeb8ef0b174..2fee47b0d50b 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -95,6 +95,7 @@ config DRM_SII902X depends on OF select DRM_KMS_HELPER select REGMAP_I2C + select I2C_MUX ---help--- Silicon Image sii902x bridge chip driver. diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index e59a13542333..bfa902013aa4 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -1,4 +1,6 @@ /* + * Copyright (C) 2018 Renesas Electronics + * * Copyright (C) 2016 Atmel * Bo Shen <voice.shen@atmel.com> * @@ -21,6 +23,7 @@ */ #include <linux/gpio/consumer.h> +#include <linux/i2c-mux.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/regmap.h> @@ -86,8 +89,49 @@ struct sii902x { struct drm_bridge bridge; struct drm_connector connector; struct gpio_desc *reset_gpio; + struct i2c_mux_core *i2cmux; }; +static int sii902x_read_unlocked(struct i2c_client *i2c, u8 reg, u8 *val) +{ + union i2c_smbus_data data; + int ret; + + ret = __i2c_smbus_xfer(i2c->adapter, i2c->addr, i2c->flags, + I2C_SMBUS_READ, reg, I2C_SMBUS_BYTE_DATA, &data); + + if (ret < 0) + return ret; + + *val = data.byte; + return 0; +} + +static int sii902x_write_unlocked(struct i2c_client *i2c, u8 reg, u8 val) +{ + union i2c_smbus_data data; + + data.byte = val; + + return __i2c_smbus_xfer(i2c->adapter, i2c->addr, i2c->flags, + I2C_SMBUS_WRITE, reg, I2C_SMBUS_BYTE_DATA, + &data); +} + +static int sii902x_update_bits_unlocked(struct i2c_client *i2c, u8 reg, u8 mask, + u8 val) +{ + int ret; + u8 status; + + ret = sii902x_read_unlocked(i2c, reg, &status); + if (ret) + return ret; + status &= ~mask; + status |= val & mask; + return sii902x_write_unlocked(i2c, reg, status); +} + static inline struct sii902x *bridge_to_sii902x(struct drm_bridge *bridge) { return container_of(bridge, struct sii902x, bridge); @@ -135,41 +179,11 @@ static const struct drm_connector_funcs sii902x_connector_funcs = { static int sii902x_get_modes(struct drm_connector *connector) { struct sii902x *sii902x = connector_to_sii902x(connector); - struct regmap *regmap = sii902x->regmap; u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; - struct device *dev = &sii902x->i2c->dev; - unsigned long timeout; - unsigned int retries; - unsigned int status; struct edid *edid; - int num = 0; - int ret; - - ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA, - SII902X_SYS_CTRL_DDC_BUS_REQ, - SII902X_SYS_CTRL_DDC_BUS_REQ); - if (ret) - return ret; - - timeout = jiffies + - msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS); - do { - ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status); - if (ret) - return ret; - } while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) && - time_before(jiffies, timeout)); + int num = 0, ret; - if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) { - dev_err(dev, "failed to acquire the i2c bus\n"); - return -ETIMEDOUT; - } - - ret = regmap_write(regmap, SII902X_SYS_CTRL_DATA, status); - if (ret) - return ret; - - edid = drm_get_edid(connector, sii902x->i2c->adapter); + edid = drm_get_edid(connector, sii902x->i2cmux->adapter[0]); drm_connector_update_edid_property(connector, edid); if (edid) { num = drm_add_edid_modes(connector, edid); @@ -181,42 +195,6 @@ static int sii902x_get_modes(struct drm_connector *connector) if (ret) return ret; - /* - * Sometimes the I2C bus can stall after failure to use the - * EDID channel. Retry a few times to see if things clear - * up, else continue anyway. - */ - retries = 5; - do { - ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, - &status); - retries--; - } while (ret && retries); - if (ret) - dev_err(dev, "failed to read status (%d)\n", ret); - - ret = regmap_update_bits(regmap, SII902X_SYS_CTRL_DATA, - SII902X_SYS_CTRL_DDC_BUS_REQ | - SII902X_SYS_CTRL_DDC_BUS_GRTD, 0); - if (ret) - return ret; - - timeout = jiffies + - msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS); - do { - ret = regmap_read(regmap, SII902X_SYS_CTRL_DATA, &status); - if (ret) - return ret; - } while (status & (SII902X_SYS_CTRL_DDC_BUS_REQ | - SII902X_SYS_CTRL_DDC_BUS_GRTD) && - time_before(jiffies, timeout)); - - if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ | - SII902X_SYS_CTRL_DDC_BUS_GRTD)) { - dev_err(dev, "failed to release the i2c bus\n"); - return -ETIMEDOUT; - } - return num; } @@ -366,6 +344,121 @@ static irqreturn_t sii902x_interrupt(int irq, void *data) return IRQ_HANDLED; } +/* + * The purpose of sii902x_i2c_bypass_select is to enable the pass through + * mode of the HDMI transmitter. Do not use regmap from within this function, + * only use sii902x_*_unlocked functions to read/modify/write registers. + * We are holding the parent adapter lock here, keep this in mind before + * adding more i2c transactions. + * + * Also, since SII902X_SYS_CTRL_DATA is used with regmap_update_bits elsewhere + * in this driver, we need to make sure that we only touch 0x1A[2:1] from + * within sii902x_i2c_bypass_select and sii902x_i2c_bypass_deselect, and that + * we leave the remaining bits as we have found them. + */ +static int sii902x_i2c_bypass_select(struct i2c_mux_core *mux, u32 chan_id) +{ + struct sii902x *sii902x = i2c_mux_priv(mux); + struct device *dev = &sii902x->i2c->dev; + unsigned long timeout; + u8 status; + int ret; + + ret = sii902x_update_bits_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA, + SII902X_SYS_CTRL_DDC_BUS_REQ, + SII902X_SYS_CTRL_DDC_BUS_REQ); + if (ret) + return ret; + + timeout = jiffies + + msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS); + do { + ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA, + &status); + if (ret) + return ret; + } while (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD) && + time_before(jiffies, timeout)); + + if (!(status & SII902X_SYS_CTRL_DDC_BUS_GRTD)) { + dev_err(dev, "Failed to acquire the i2c bus\n"); + return -ETIMEDOUT; + } + + return sii902x_write_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA, + status); +} + +/* + * The purpose of sii902x_i2c_bypass_deselect is to disable the pass through + * mode of the HDMI transmitter. Do not use regmap from within this function, + * only use sii902x_*_unlocked functions to read/modify/write registers. + * We are holding the parent adapter lock here, keep this in mind before + * adding more i2c transactions. + * + * Also, since SII902X_SYS_CTRL_DATA is used with regmap_update_bits elsewhere + * in this driver, we need to make sure that we only touch 0x1A[2:1] from + * within sii902x_i2c_bypass_select and sii902x_i2c_bypass_deselect, and that + * we leave the remaining bits as we have found them. + */ +static int sii902x_i2c_bypass_deselect(struct i2c_mux_core *mux, u32 chan_id) +{ + struct sii902x *sii902x = i2c_mux_priv(mux); + struct device *dev = &sii902x->i2c->dev; + unsigned long timeout; + unsigned int retries; + u8 status; + int ret; + + /* + * When the HDMI transmitter is in pass through mode, we need an + * (undocumented) additional delay between STOP and START conditions + * to guarantee the bus won't get stuck. + */ + udelay(30); + + /* + * Sometimes the I2C bus can stall after failure to use the + * EDID channel. Retry a few times to see if things clear + * up, else continue anyway. + */ + retries = 5; + do { + ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA, + &status); + retries--; + } while (ret && retries); + if (ret) { + dev_err(dev, "failed to read status (%d)\n", ret); + return ret; + } + + ret = sii902x_update_bits_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA, + SII902X_SYS_CTRL_DDC_BUS_REQ | + SII902X_SYS_CTRL_DDC_BUS_GRTD, 0); + if (ret) + return ret; + + timeout = jiffies + + msecs_to_jiffies(SII902X_I2C_BUS_ACQUISITION_TIMEOUT_MS); + do { + ret = sii902x_read_unlocked(sii902x->i2c, SII902X_SYS_CTRL_DATA, + &status); + if (ret) + return ret; + } while (status & (SII902X_SYS_CTRL_DDC_BUS_REQ | + SII902X_SYS_CTRL_DDC_BUS_GRTD) && + time_before(jiffies, timeout)); + + if (status & (SII902X_SYS_CTRL_DDC_BUS_REQ | + SII902X_SYS_CTRL_DDC_BUS_GRTD)) { + dev_err(dev, "failed to release the i2c bus\n"); + return -ETIMEDOUT; + } + + return 0; +} + static int sii902x_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -375,6 +468,13 @@ static int sii902x_probe(struct i2c_client *client, u8 chipid[4]; int ret; + ret = i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA); + if (!ret) { + dev_err(dev, "I2C adapter not suitable\n"); + return -EIO; + } + sii902x = devm_kzalloc(dev, sizeof(*sii902x), GFP_KERNEL); if (!sii902x) return -ENOMEM; @@ -433,7 +533,15 @@ static int sii902x_probe(struct i2c_client *client, i2c_set_clientdata(client, sii902x); - return 0; + sii902x->i2cmux = i2c_mux_alloc(client->adapter, dev, + 1, 0, I2C_MUX_GATE, + sii902x_i2c_bypass_select, + sii902x_i2c_bypass_deselect); + if (!sii902x->i2cmux) + return -ENOMEM; + + sii902x->i2cmux->priv = sii902x; + return i2c_mux_add_adapter(sii902x->i2cmux, 0, 0, 0); } static int sii902x_remove(struct i2c_client *client) @@ -441,6 +549,7 @@ static int sii902x_remove(struct i2c_client *client) { struct sii902x *sii902x = i2c_get_clientdata(client); + i2c_mux_del_adapters(sii902x->i2cmux); drm_bridge_remove(&sii902x->bridge); return 0; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index 8f9c8a6b46de..2228689d9a5e 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * dw-hdmi-i2s-audio.c * * Copyright (c) 2017 Renesas Solutions Corp. * Kuninori Morimoto <kuninori.morimoto.gx@renesas.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 <drm/bridge/dw_hdmi.h> diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index ee6b98efa9c2..afd491018bfc 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -379,7 +379,7 @@ static void tc358764_detach(struct drm_bridge *bridge) drm_fb_helper_remove_one_connector(drm->fb_helper, &ctx->connector); drm_panel_detach(ctx->panel); ctx->panel = NULL; - drm_connector_unreference(&ctx->connector); + drm_connector_put(&ctx->connector); } static const struct drm_bridge_funcs tc358764_bridge_funcs = { diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 68ab1821e15b..4dd499c7d1ba 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -169,7 +169,6 @@ static int cirrusfb_create(struct drm_fb_helper *helper, struct drm_mode_fb_cmd2 mode_cmd; void *sysram; struct drm_gem_object *gobj = NULL; - struct cirrus_bo *bo = NULL; int size, ret; mode_cmd.width = sizes->surface_width; @@ -185,8 +184,6 @@ static int cirrusfb_create(struct drm_fb_helper *helper, return ret; } - bo = gem_to_cirrus_bo(gobj); - sysram = vmalloc(size); if (!sysram) return -ENOMEM; diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 1706ed1100d5..008224f376fe 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -315,9 +315,11 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_get_crtc_state); -static int drm_atomic_crtc_check(struct drm_crtc *crtc, - struct drm_crtc_state *state) +static int drm_atomic_crtc_check(const struct drm_crtc_state *old_crtc_state, + const struct drm_crtc_state *new_crtc_state) { + struct drm_crtc *crtc = new_crtc_state->crtc; + /* NOTE: we explicitly don't enforce constraints such as primary * layer covering entire screen, since that is something we want * to allow (on hw that supports it). For hw that does not, it @@ -326,7 +328,7 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc, * TODO: Add generic modeset state checks once we support those. */ - if (state->active && !state->enable) { + if (new_crtc_state->active && !new_crtc_state->enable) { DRM_DEBUG_ATOMIC("[CRTC:%d:%s] active without enabled\n", crtc->base.id, crtc->name); return -EINVAL; @@ -336,14 +338,14 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc, * as this is a kernel-internal detail that userspace should never * be able to trigger. */ if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) && - WARN_ON(state->enable && !state->mode_blob)) { + WARN_ON(new_crtc_state->enable && !new_crtc_state->mode_blob)) { DRM_DEBUG_ATOMIC("[CRTC:%d:%s] enabled without mode blob\n", crtc->base.id, crtc->name); return -EINVAL; } if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) && - WARN_ON(!state->enable && state->mode_blob)) { + WARN_ON(!new_crtc_state->enable && new_crtc_state->mode_blob)) { DRM_DEBUG_ATOMIC("[CRTC:%d:%s] disabled with mode blob\n", crtc->base.id, crtc->name); return -EINVAL; @@ -359,7 +361,8 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc, * and legacy page_flip IOCTL which also reject service on a disabled * pipe. */ - if (state->event && !state->active && !crtc->state->active) { + if (new_crtc_state->event && + !new_crtc_state->active && !old_crtc_state->active) { DRM_DEBUG_ATOMIC("[CRTC:%d:%s] requesting event but off\n", crtc->base.id, crtc->name); return -EINVAL; @@ -494,14 +497,13 @@ drm_atomic_get_plane_state(struct drm_atomic_state *state, EXPORT_SYMBOL(drm_atomic_get_plane_state); static bool -plane_switching_crtc(struct drm_atomic_state *state, - struct drm_plane *plane, - struct drm_plane_state *plane_state) +plane_switching_crtc(const struct drm_plane_state *old_plane_state, + const struct drm_plane_state *new_plane_state) { - if (!plane->state->crtc || !plane_state->crtc) + if (!old_plane_state->crtc || !new_plane_state->crtc) return false; - if (plane->state->crtc == plane_state->crtc) + if (old_plane_state->crtc == new_plane_state->crtc) return false; /* This could be refined, but currently there's no helper or driver code @@ -514,88 +516,95 @@ plane_switching_crtc(struct drm_atomic_state *state, /** * drm_atomic_plane_check - check plane state - * @plane: plane to check - * @state: plane state to check + * @old_plane_state: old plane state to check + * @new_plane_state: new plane state to check * * Provides core sanity checks for plane state. * * RETURNS: * Zero on success, error code on failure */ -static int drm_atomic_plane_check(struct drm_plane *plane, - struct drm_plane_state *state) +static int drm_atomic_plane_check(const struct drm_plane_state *old_plane_state, + const struct drm_plane_state *new_plane_state) { + struct drm_plane *plane = new_plane_state->plane; + struct drm_crtc *crtc = new_plane_state->crtc; + const struct drm_framebuffer *fb = new_plane_state->fb; unsigned int fb_width, fb_height; int ret; /* either *both* CRTC and FB must be set, or neither */ - if (state->crtc && !state->fb) { + if (crtc && !fb) { DRM_DEBUG_ATOMIC("[PLANE:%d:%s] CRTC set but no FB\n", plane->base.id, plane->name); return -EINVAL; - } else if (state->fb && !state->crtc) { + } else if (fb && !crtc) { DRM_DEBUG_ATOMIC("[PLANE:%d:%s] FB set but no CRTC\n", plane->base.id, plane->name); return -EINVAL; } /* if disabled, we don't care about the rest of the state: */ - if (!state->crtc) + if (!crtc) return 0; /* Check whether this plane is usable on this CRTC */ - if (!(plane->possible_crtcs & drm_crtc_mask(state->crtc))) { + if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) { DRM_DEBUG_ATOMIC("Invalid [CRTC:%d:%s] for [PLANE:%d:%s]\n", - state->crtc->base.id, state->crtc->name, + crtc->base.id, crtc->name, plane->base.id, plane->name); return -EINVAL; } /* Check whether this plane supports the fb pixel format. */ - ret = drm_plane_check_pixel_format(plane, state->fb->format->format, - state->fb->modifier); + ret = drm_plane_check_pixel_format(plane, fb->format->format, + fb->modifier); if (ret) { struct drm_format_name_buf format_name; DRM_DEBUG_ATOMIC("[PLANE:%d:%s] invalid pixel format %s, modifier 0x%llx\n", plane->base.id, plane->name, - drm_get_format_name(state->fb->format->format, + drm_get_format_name(fb->format->format, &format_name), - state->fb->modifier); + fb->modifier); return ret; } /* Give drivers some help against integer overflows */ - if (state->crtc_w > INT_MAX || - state->crtc_x > INT_MAX - (int32_t) state->crtc_w || - state->crtc_h > INT_MAX || - state->crtc_y > INT_MAX - (int32_t) state->crtc_h) { + if (new_plane_state->crtc_w > INT_MAX || + new_plane_state->crtc_x > INT_MAX - (int32_t) new_plane_state->crtc_w || + new_plane_state->crtc_h > INT_MAX || + new_plane_state->crtc_y > INT_MAX - (int32_t) new_plane_state->crtc_h) { DRM_DEBUG_ATOMIC("[PLANE:%d:%s] invalid CRTC coordinates %ux%u+%d+%d\n", plane->base.id, plane->name, - state->crtc_w, state->crtc_h, - state->crtc_x, state->crtc_y); + new_plane_state->crtc_w, new_plane_state->crtc_h, + new_plane_state->crtc_x, new_plane_state->crtc_y); return -ERANGE; } - fb_width = state->fb->width << 16; - fb_height = state->fb->height << 16; + fb_width = fb->width << 16; + fb_height = fb->height << 16; /* Make sure source coordinates are inside the fb. */ - if (state->src_w > fb_width || - state->src_x > fb_width - state->src_w || - state->src_h > fb_height || - state->src_y > fb_height - state->src_h) { + if (new_plane_state->src_w > fb_width || + new_plane_state->src_x > fb_width - new_plane_state->src_w || + new_plane_state->src_h > fb_height || + new_plane_state->src_y > fb_height - new_plane_state->src_h) { DRM_DEBUG_ATOMIC("[PLANE:%d:%s] invalid source coordinates " "%u.%06ux%u.%06u+%u.%06u+%u.%06u (fb %ux%u)\n", plane->base.id, plane->name, - state->src_w >> 16, ((state->src_w & 0xffff) * 15625) >> 10, - state->src_h >> 16, ((state->src_h & 0xffff) * 15625) >> 10, - state->src_x >> 16, ((state->src_x & 0xffff) * 15625) >> 10, - state->src_y >> 16, ((state->src_y & 0xffff) * 15625) >> 10, - state->fb->width, state->fb->height); + new_plane_state->src_w >> 16, + ((new_plane_state->src_w & 0xffff) * 15625) >> 10, + new_plane_state->src_h >> 16, + ((new_plane_state->src_h & 0xffff) * 15625) >> 10, + new_plane_state->src_x >> 16, + ((new_plane_state->src_x & 0xffff) * 15625) >> 10, + new_plane_state->src_y >> 16, + ((new_plane_state->src_y & 0xffff) * 15625) >> 10, + fb->width, fb->height); return -ENOSPC; } - if (plane_switching_crtc(state->state, plane, state)) { + if (plane_switching_crtc(old_plane_state, new_plane_state)) { DRM_DEBUG_ATOMIC("[PLANE:%d:%s] switching CRTC directly\n", plane->base.id, plane->name); return -EINVAL; @@ -667,6 +676,7 @@ static void drm_atomic_plane_print_state(struct drm_printer *p, /** * drm_atomic_private_obj_init - initialize private object + * @dev: DRM device this object will be attached to * @obj: private object * @state: initial private object state * @funcs: pointer to the struct of function pointers that identify the object @@ -676,14 +686,18 @@ static void drm_atomic_plane_print_state(struct drm_printer *p, * driver private object that needs its own atomic state. */ void -drm_atomic_private_obj_init(struct drm_private_obj *obj, +drm_atomic_private_obj_init(struct drm_device *dev, + struct drm_private_obj *obj, struct drm_private_state *state, const struct drm_private_state_funcs *funcs) { memset(obj, 0, sizeof(*obj)); + drm_modeset_lock_init(&obj->lock); + obj->state = state; obj->funcs = funcs; + list_add_tail(&obj->head, &dev->mode_config.privobj_list); } EXPORT_SYMBOL(drm_atomic_private_obj_init); @@ -696,7 +710,9 @@ EXPORT_SYMBOL(drm_atomic_private_obj_init); void drm_atomic_private_obj_fini(struct drm_private_obj *obj) { + list_del(&obj->head); obj->funcs->atomic_destroy_state(obj, obj->state); + drm_modeset_lock_fini(&obj->lock); } EXPORT_SYMBOL(drm_atomic_private_obj_fini); @@ -706,8 +722,8 @@ EXPORT_SYMBOL(drm_atomic_private_obj_fini); * @obj: private object to get the state for * * This function returns the private object state for the given private object, - * allocating the state if needed. It does not grab any locks as the caller is - * expected to care of any required locking. + * allocating the state if needed. It will also grab the relevant private + * object lock to make sure that the state is consistent. * * RETURNS: * @@ -717,7 +733,7 @@ struct drm_private_state * drm_atomic_get_private_obj_state(struct drm_atomic_state *state, struct drm_private_obj *obj) { - int index, num_objs, i; + int index, num_objs, i, ret; size_t size; struct __drm_private_objs_state *arr; struct drm_private_state *obj_state; @@ -726,6 +742,10 @@ drm_atomic_get_private_obj_state(struct drm_atomic_state *state, if (obj == state->private_objs[i].ptr) return state->private_objs[i].state; + ret = drm_modeset_lock(&obj->lock, state->acquire_ctx); + if (ret) + return ERR_PTR(ret); + num_objs = state->num_private_objs + 1; size = sizeof(*state->private_objs) * num_objs; arr = krealloc(state->private_objs, size, GFP_KERNEL); @@ -932,6 +952,8 @@ int drm_atomic_add_affected_planes(struct drm_atomic_state *state, struct drm_crtc *crtc) { + const struct drm_crtc_state *old_crtc_state = + drm_atomic_get_old_crtc_state(state, crtc); struct drm_plane *plane; WARN_ON(!drm_atomic_get_new_crtc_state(state, crtc)); @@ -939,7 +961,7 @@ drm_atomic_add_affected_planes(struct drm_atomic_state *state, DRM_DEBUG_ATOMIC("Adding all current planes for [CRTC:%d:%s] to %p\n", crtc->base.id, crtc->name, state); - drm_for_each_plane_mask(plane, state->dev, crtc->state->plane_mask) { + drm_for_each_plane_mask(plane, state->dev, old_crtc_state->plane_mask) { struct drm_plane_state *plane_state = drm_atomic_get_plane_state(state, plane); @@ -966,17 +988,19 @@ int drm_atomic_check_only(struct drm_atomic_state *state) struct drm_device *dev = state->dev; struct drm_mode_config *config = &dev->mode_config; struct drm_plane *plane; - struct drm_plane_state *plane_state; + struct drm_plane_state *old_plane_state; + struct drm_plane_state *new_plane_state; struct drm_crtc *crtc; - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *old_crtc_state; + struct drm_crtc_state *new_crtc_state; struct drm_connector *conn; struct drm_connector_state *conn_state; int i, ret = 0; DRM_DEBUG_ATOMIC("checking %p\n", state); - for_each_new_plane_in_state(state, plane, plane_state, i) { - ret = drm_atomic_plane_check(plane, plane_state); + for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { + ret = drm_atomic_plane_check(old_plane_state, new_plane_state); if (ret) { DRM_DEBUG_ATOMIC("[PLANE:%d:%s] atomic core check failed\n", plane->base.id, plane->name); @@ -984,8 +1008,8 @@ int drm_atomic_check_only(struct drm_atomic_state *state) } } - for_each_new_crtc_in_state(state, crtc, crtc_state, i) { - ret = drm_atomic_crtc_check(crtc, crtc_state); + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { + ret = drm_atomic_crtc_check(old_crtc_state, new_crtc_state); if (ret) { DRM_DEBUG_ATOMIC("[CRTC:%d:%s] atomic core check failed\n", crtc->base.id, crtc->name); @@ -1013,8 +1037,8 @@ int drm_atomic_check_only(struct drm_atomic_state *state) } if (!state->allow_modeset) { - for_each_new_crtc_in_state(state, crtc, crtc_state, i) { - if (drm_atomic_crtc_needs_modeset(crtc_state)) { + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + if (drm_atomic_crtc_needs_modeset(new_crtc_state)) { DRM_DEBUG_ATOMIC("[CRTC:%d:%s] requires full modeset\n", crtc->base.id, crtc->name); return -EINVAL; diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index fa95f9974f6d..8150fa8387d5 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1460,6 +1460,9 @@ void drm_atomic_helper_wait_for_flip_done(struct drm_device *dev, DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n", crtc->base.id, crtc->name); } + + if (old_state->fake_commit) + complete_all(&old_state->fake_commit->flip_done); } EXPORT_SYMBOL(drm_atomic_helper_wait_for_flip_done); @@ -2217,8 +2220,10 @@ void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *old_state) spin_unlock(&crtc->commit_lock); } - if (old_state->fake_commit) + if (old_state->fake_commit) { complete_all(&old_state->fake_commit->cleanup_done); + WARN_ON(!try_wait_for_completion(&old_state->fake_commit->hw_done)); + } } EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done); @@ -3123,27 +3128,104 @@ void drm_atomic_helper_shutdown(struct drm_device *dev) struct drm_modeset_acquire_ctx ctx; int ret; - drm_modeset_acquire_init(&ctx, 0); - while (1) { - ret = drm_modeset_lock_all_ctx(dev, &ctx); - if (!ret) - ret = __drm_atomic_helper_disable_all(dev, &ctx, true); - - if (ret != -EDEADLK) - break; - - drm_modeset_backoff(&ctx); - } + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret); + ret = __drm_atomic_helper_disable_all(dev, &ctx, true); if (ret) DRM_ERROR("Disabling all crtc's during unload failed with %i\n", ret); - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); + DRM_MODESET_LOCK_ALL_END(ctx, ret); } EXPORT_SYMBOL(drm_atomic_helper_shutdown); /** + * drm_atomic_helper_duplicate_state - duplicate an atomic state object + * @dev: DRM device + * @ctx: lock acquisition context + * + * Makes a copy of the current atomic state by looping over all objects and + * duplicating their respective states. This is used for example by suspend/ + * resume support code to save the state prior to suspend such that it can + * be restored upon resume. + * + * Note that this treats atomic state as persistent between save and restore. + * Drivers must make sure that this is possible and won't result in confusion + * or erroneous behaviour. + * + * Note that if callers haven't already acquired all modeset locks this might + * return -EDEADLK, which must be handled by calling drm_modeset_backoff(). + * + * Returns: + * A pointer to the copy of the atomic state object on success or an + * ERR_PTR()-encoded error code on failure. + * + * See also: + * drm_atomic_helper_suspend(), drm_atomic_helper_resume() + */ +struct drm_atomic_state * +drm_atomic_helper_duplicate_state(struct drm_device *dev, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_atomic_state *state; + struct drm_connector *conn; + struct drm_connector_list_iter conn_iter; + struct drm_plane *plane; + struct drm_crtc *crtc; + int err = 0; + + state = drm_atomic_state_alloc(dev); + if (!state) + return ERR_PTR(-ENOMEM); + + state->acquire_ctx = ctx; + + drm_for_each_crtc(crtc, dev) { + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + err = PTR_ERR(crtc_state); + goto free; + } + } + + drm_for_each_plane(plane, dev) { + struct drm_plane_state *plane_state; + + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + err = PTR_ERR(plane_state); + goto free; + } + } + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(conn, &conn_iter) { + struct drm_connector_state *conn_state; + + conn_state = drm_atomic_get_connector_state(state, conn); + if (IS_ERR(conn_state)) { + err = PTR_ERR(conn_state); + drm_connector_list_iter_end(&conn_iter); + goto free; + } + } + drm_connector_list_iter_end(&conn_iter); + + /* clear the acquire context so that it isn't accidentally reused */ + state->acquire_ctx = NULL; + +free: + if (err < 0) { + drm_atomic_state_put(state); + state = ERR_PTR(err); + } + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_duplicate_state); + +/** * drm_atomic_helper_suspend - subsystem-level suspend helper * @dev: DRM device * @@ -3174,14 +3256,10 @@ struct drm_atomic_state *drm_atomic_helper_suspend(struct drm_device *dev) struct drm_atomic_state *state; int err; - drm_modeset_acquire_init(&ctx, 0); + /* This can never be returned, but it makes the compiler happy */ + state = ERR_PTR(-EINVAL); -retry: - err = drm_modeset_lock_all_ctx(dev, &ctx); - if (err < 0) { - state = ERR_PTR(err); - goto unlock; - } + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, err); state = drm_atomic_helper_duplicate_state(dev, &ctx); if (IS_ERR(state)) @@ -3195,13 +3273,10 @@ retry: } unlock: - if (PTR_ERR(state) == -EDEADLK) { - drm_modeset_backoff(&ctx); - goto retry; - } + DRM_MODESET_LOCK_ALL_END(ctx, err); + if (err) + return ERR_PTR(err); - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); return state; } EXPORT_SYMBOL(drm_atomic_helper_suspend); @@ -3224,7 +3299,7 @@ EXPORT_SYMBOL(drm_atomic_helper_suspend); int drm_atomic_helper_commit_duplicated_state(struct drm_atomic_state *state, struct drm_modeset_acquire_ctx *ctx) { - int i; + int i, ret; struct drm_plane *plane; struct drm_plane_state *new_plane_state; struct drm_connector *connector; @@ -3243,7 +3318,11 @@ int drm_atomic_helper_commit_duplicated_state(struct drm_atomic_state *state, for_each_new_connector_in_state(state, connector, new_conn_state, i) state->connectors[i].old_state = connector->state; - return drm_atomic_commit(state); + ret = drm_atomic_commit(state); + + state->acquire_ctx = NULL; + + return ret; } EXPORT_SYMBOL(drm_atomic_helper_commit_duplicated_state); @@ -3271,23 +3350,12 @@ int drm_atomic_helper_resume(struct drm_device *dev, drm_mode_config_reset(dev); - drm_modeset_acquire_init(&ctx, 0); - while (1) { - err = drm_modeset_lock_all_ctx(dev, &ctx); - if (err) - goto out; - - err = drm_atomic_helper_commit_duplicated_state(state, &ctx); -out: - if (err != -EDEADLK) - break; + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, err); - drm_modeset_backoff(&ctx); - } + err = drm_atomic_helper_commit_duplicated_state(state, &ctx); + DRM_MODESET_LOCK_ALL_END(ctx, err); drm_atomic_state_put(state); - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); return err; } @@ -3426,3 +3494,73 @@ fail: return ret; } EXPORT_SYMBOL(drm_atomic_helper_page_flip_target); + +/** + * drm_atomic_helper_legacy_gamma_set - set the legacy gamma correction table + * @crtc: CRTC object + * @red: red correction table + * @green: green correction table + * @blue: green correction table + * @size: size of the tables + * @ctx: lock acquire context + * + * Implements support for legacy gamma correction table for drivers + * that support color management through the DEGAMMA_LUT/GAMMA_LUT + * properties. See drm_crtc_enable_color_mgmt() and the containing chapter for + * how the atomic color management and gamma tables work. + */ +int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, + u16 *red, u16 *green, u16 *blue, + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_device *dev = crtc->dev; + struct drm_atomic_state *state; + struct drm_crtc_state *crtc_state; + struct drm_property_blob *blob = NULL; + struct drm_color_lut *blob_data; + int i, ret = 0; + bool replaced; + + state = drm_atomic_state_alloc(crtc->dev); + if (!state) + return -ENOMEM; + + blob = drm_property_create_blob(dev, + sizeof(struct drm_color_lut) * size, + NULL); + if (IS_ERR(blob)) { + ret = PTR_ERR(blob); + blob = NULL; + goto fail; + } + + /* Prepare GAMMA_LUT with the legacy values. */ + blob_data = blob->data; + for (i = 0; i < size; i++) { + blob_data[i].red = red[i]; + blob_data[i].green = green[i]; + blob_data[i].blue = blue[i]; + } + + state->acquire_ctx = ctx; + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto fail; + } + + /* Reset DEGAMMA_LUT and CTM properties. */ + replaced = drm_property_replace_blob(&crtc_state->degamma_lut, NULL); + replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL); + replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob); + crtc_state->color_mgmt_changed |= replaced; + + ret = drm_atomic_commit(state); + +fail: + drm_atomic_state_put(state); + drm_property_blob_put(blob); + return ret; +} +EXPORT_SYMBOL(drm_atomic_helper_legacy_gamma_set); diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index 3ba996069d69..60bd7d708e35 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -394,93 +394,6 @@ drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector) EXPORT_SYMBOL(drm_atomic_helper_connector_duplicate_state); /** - * drm_atomic_helper_duplicate_state - duplicate an atomic state object - * @dev: DRM device - * @ctx: lock acquisition context - * - * Makes a copy of the current atomic state by looping over all objects and - * duplicating their respective states. This is used for example by suspend/ - * resume support code to save the state prior to suspend such that it can - * be restored upon resume. - * - * Note that this treats atomic state as persistent between save and restore. - * Drivers must make sure that this is possible and won't result in confusion - * or erroneous behaviour. - * - * Note that if callers haven't already acquired all modeset locks this might - * return -EDEADLK, which must be handled by calling drm_modeset_backoff(). - * - * Returns: - * A pointer to the copy of the atomic state object on success or an - * ERR_PTR()-encoded error code on failure. - * - * See also: - * drm_atomic_helper_suspend(), drm_atomic_helper_resume() - */ -struct drm_atomic_state * -drm_atomic_helper_duplicate_state(struct drm_device *dev, - struct drm_modeset_acquire_ctx *ctx) -{ - struct drm_atomic_state *state; - struct drm_connector *conn; - struct drm_connector_list_iter conn_iter; - struct drm_plane *plane; - struct drm_crtc *crtc; - int err = 0; - - state = drm_atomic_state_alloc(dev); - if (!state) - return ERR_PTR(-ENOMEM); - - state->acquire_ctx = ctx; - - drm_for_each_crtc(crtc, dev) { - struct drm_crtc_state *crtc_state; - - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) { - err = PTR_ERR(crtc_state); - goto free; - } - } - - drm_for_each_plane(plane, dev) { - struct drm_plane_state *plane_state; - - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - err = PTR_ERR(plane_state); - goto free; - } - } - - drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(conn, &conn_iter) { - struct drm_connector_state *conn_state; - - conn_state = drm_atomic_get_connector_state(state, conn); - if (IS_ERR(conn_state)) { - err = PTR_ERR(conn_state); - drm_connector_list_iter_end(&conn_iter); - goto free; - } - } - drm_connector_list_iter_end(&conn_iter); - - /* clear the acquire context so that it isn't accidentally reused */ - state->acquire_ctx = NULL; - -free: - if (err < 0) { - drm_atomic_state_put(state); - state = ERR_PTR(err); - } - - return state; -} -EXPORT_SYMBOL(drm_atomic_helper_duplicate_state); - -/** * __drm_atomic_helper_connector_destroy_state - release connector state * @state: connector state object to release * @@ -516,76 +429,6 @@ void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); /** - * drm_atomic_helper_legacy_gamma_set - set the legacy gamma correction table - * @crtc: CRTC object - * @red: red correction table - * @green: green correction table - * @blue: green correction table - * @size: size of the tables - * @ctx: lock acquire context - * - * Implements support for legacy gamma correction table for drivers - * that support color management through the DEGAMMA_LUT/GAMMA_LUT - * properties. See drm_crtc_enable_color_mgmt() and the containing chapter for - * how the atomic color management and gamma tables work. - */ -int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, - u16 *red, u16 *green, u16 *blue, - uint32_t size, - struct drm_modeset_acquire_ctx *ctx) -{ - struct drm_device *dev = crtc->dev; - struct drm_atomic_state *state; - struct drm_crtc_state *crtc_state; - struct drm_property_blob *blob = NULL; - struct drm_color_lut *blob_data; - int i, ret = 0; - bool replaced; - - state = drm_atomic_state_alloc(crtc->dev); - if (!state) - return -ENOMEM; - - blob = drm_property_create_blob(dev, - sizeof(struct drm_color_lut) * size, - NULL); - if (IS_ERR(blob)) { - ret = PTR_ERR(blob); - blob = NULL; - goto fail; - } - - /* Prepare GAMMA_LUT with the legacy values. */ - blob_data = blob->data; - for (i = 0; i < size; i++) { - blob_data[i].red = red[i]; - blob_data[i].green = green[i]; - blob_data[i].blue = blue[i]; - } - - state->acquire_ctx = ctx; - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) { - ret = PTR_ERR(crtc_state); - goto fail; - } - - /* Reset DEGAMMA_LUT and CTM properties. */ - replaced = drm_property_replace_blob(&crtc_state->degamma_lut, NULL); - replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL); - replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob); - crtc_state->color_mgmt_changed |= replaced; - - ret = drm_atomic_commit(state); - -fail: - drm_atomic_state_put(state); - drm_property_blob_put(blob); - return ret; -} -EXPORT_SYMBOL(drm_atomic_helper_legacy_gamma_set); - -/** * __drm_atomic_helper_private_duplicate_state - copy atomic private state * @obj: CRTC object * @state: new private object state diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index fc03d26fcacc..9b2bd28dde0a 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -81,8 +81,7 @@ int drm_client_init(struct drm_device *dev, struct drm_client_dev *client, { int ret; - if (!drm_core_check_feature(dev, DRIVER_MODESET) || - !dev->driver->dumb_create || !dev->driver->gem_prime_vmap) + if (!drm_core_check_feature(dev, DRIVER_MODESET) || !dev->driver->dumb_create) return -EOPNOTSUPP; if (funcs && !try_module_get(funcs->owner)) @@ -229,8 +228,7 @@ static void drm_client_buffer_delete(struct drm_client_buffer *buffer) { struct drm_device *dev = buffer->client->dev; - if (buffer->vaddr && dev->driver->gem_prime_vunmap) - dev->driver->gem_prime_vunmap(buffer->gem, buffer->vaddr); + drm_gem_vunmap(buffer->gem, buffer->vaddr); if (buffer->gem) drm_gem_object_put_unlocked(buffer->gem); @@ -283,9 +281,9 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u * fd_install step out of the driver backend hooks, to make that * final step optional for internal users. */ - vaddr = dev->driver->gem_prime_vmap(obj); - if (!vaddr) { - ret = -ENOMEM; + vaddr = drm_gem_vmap(obj); + if (IS_ERR(vaddr)) { + ret = PTR_ERR(vaddr); goto err_delete; } diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c index 581cc3788223..07dcf47daafe 100644 --- a/drivers/gpu/drm/drm_color_mgmt.c +++ b/drivers/gpu/drm/drm_color_mgmt.c @@ -255,11 +255,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, if (crtc_lut->gamma_size != crtc->gamma_size) return -EINVAL; - drm_modeset_acquire_init(&ctx, 0); -retry: - ret = drm_modeset_lock_all_ctx(dev, &ctx); - if (ret) - goto out; + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret); size = crtc_lut->gamma_size * (sizeof(uint16_t)); r_base = crtc->gamma_store; @@ -284,13 +280,7 @@ retry: crtc->gamma_size, &ctx); out: - if (ret == -EDEADLK) { - drm_modeset_backoff(&ctx); - goto retry; - } - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); - + DRM_MODESET_LOCK_ALL_END(ctx, ret); return ret; } diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index fa9baacc863b..66b2fd20369a 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1138,7 +1138,71 @@ void drm_hdmi_avi_infoframe_content_type(struct hdmi_avi_infoframe *frame, EXPORT_SYMBOL(drm_hdmi_avi_infoframe_content_type); /** - * drm_create_tv_properties - create TV specific connector properties + * drm_mode_attach_tv_margin_properties - attach TV connector margin properties + * @connector: DRM connector + * + * Called by a driver when it needs to attach TV margin props to a connector. + * Typically used on SDTV and HDMI connectors. + */ +void drm_connector_attach_tv_margin_properties(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + + drm_object_attach_property(&connector->base, + dev->mode_config.tv_left_margin_property, + 0); + drm_object_attach_property(&connector->base, + dev->mode_config.tv_right_margin_property, + 0); + drm_object_attach_property(&connector->base, + dev->mode_config.tv_top_margin_property, + 0); + drm_object_attach_property(&connector->base, + dev->mode_config.tv_bottom_margin_property, + 0); +} +EXPORT_SYMBOL(drm_connector_attach_tv_margin_properties); + +/** + * drm_mode_create_tv_margin_properties - create TV connector margin properties + * @dev: DRM device + * + * Called by a driver's HDMI connector initialization routine, this function + * creates the TV margin properties for a given device. No need to call this + * function for an SDTV connector, it's already called from + * drm_mode_create_tv_properties(). + */ +int drm_mode_create_tv_margin_properties(struct drm_device *dev) +{ + if (dev->mode_config.tv_left_margin_property) + return 0; + + dev->mode_config.tv_left_margin_property = + drm_property_create_range(dev, 0, "left margin", 0, 100); + if (!dev->mode_config.tv_left_margin_property) + return -ENOMEM; + + dev->mode_config.tv_right_margin_property = + drm_property_create_range(dev, 0, "right margin", 0, 100); + if (!dev->mode_config.tv_right_margin_property) + return -ENOMEM; + + dev->mode_config.tv_top_margin_property = + drm_property_create_range(dev, 0, "top margin", 0, 100); + if (!dev->mode_config.tv_top_margin_property) + return -ENOMEM; + + dev->mode_config.tv_bottom_margin_property = + drm_property_create_range(dev, 0, "bottom margin", 0, 100); + if (!dev->mode_config.tv_bottom_margin_property) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL(drm_mode_create_tv_margin_properties); + +/** + * drm_mode_create_tv_properties - create TV specific connector properties * @dev: DRM device * @num_modes: number of different TV formats (modes) supported * @modes: array of pointers to strings containing name of each format @@ -1183,24 +1247,7 @@ int drm_mode_create_tv_properties(struct drm_device *dev, /* * Other, TV specific properties: margins & TV modes. */ - dev->mode_config.tv_left_margin_property = - drm_property_create_range(dev, 0, "left margin", 0, 100); - if (!dev->mode_config.tv_left_margin_property) - goto nomem; - - dev->mode_config.tv_right_margin_property = - drm_property_create_range(dev, 0, "right margin", 0, 100); - if (!dev->mode_config.tv_right_margin_property) - goto nomem; - - dev->mode_config.tv_top_margin_property = - drm_property_create_range(dev, 0, "top margin", 0, 100); - if (!dev->mode_config.tv_top_margin_property) - goto nomem; - - dev->mode_config.tv_bottom_margin_property = - drm_property_create_range(dev, 0, "bottom margin", 0, 100); - if (!dev->mode_config.tv_bottom_margin_property) + if (drm_mode_create_tv_margin_properties(dev)) goto nomem; dev->mode_config.tv_mode_property = @@ -1960,7 +2007,7 @@ EXPORT_SYMBOL(drm_mode_get_tile_group); * identifier for the tile group. * * RETURNS: - * new tile group or error. + * new tile group or NULL. */ struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, char topology[8]) @@ -1970,7 +2017,7 @@ struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, tg = kzalloc(sizeof(*tg), GFP_KERNEL); if (!tg) - return ERR_PTR(-ENOMEM); + return NULL; kref_init(&tg->refcount); memcpy(tg->group_data, topology, 8); @@ -1982,7 +2029,7 @@ struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, tg->id = ret; } else { kfree(tg); - tg = ERR_PTR(ret); + tg = NULL; } mutex_unlock(&dev->mode_config.idr_mutex); diff --git a/drivers/gpu/drm/drm_context.c b/drivers/gpu/drm/drm_context.c index 506663c69b0a..6e8e1a9fcae3 100644 --- a/drivers/gpu/drm/drm_context.c +++ b/drivers/gpu/drm/drm_context.c @@ -361,23 +361,26 @@ int drm_legacy_addctx(struct drm_device *dev, void *data, { struct drm_ctx_list *ctx_entry; struct drm_ctx *ctx = data; + int tmp_handle; if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) && !drm_core_check_feature(dev, DRIVER_LEGACY)) return -EOPNOTSUPP; - ctx->handle = drm_legacy_ctxbitmap_next(dev); - if (ctx->handle == DRM_KERNEL_CONTEXT) { + tmp_handle = drm_legacy_ctxbitmap_next(dev); + if (tmp_handle == DRM_KERNEL_CONTEXT) { /* Skip kernel's context and get a new one. */ - ctx->handle = drm_legacy_ctxbitmap_next(dev); + tmp_handle = drm_legacy_ctxbitmap_next(dev); } - DRM_DEBUG("%d\n", ctx->handle); - if (ctx->handle < 0) { + DRM_DEBUG("%d\n", tmp_handle); + if (tmp_handle < 0) { DRM_DEBUG("Not enough free contexts.\n"); /* Should this return -EBUSY instead? */ - return -ENOMEM; + return tmp_handle; } + ctx->handle = tmp_handle; + ctx_entry = kmalloc(sizeof(*ctx_entry), GFP_KERNEL); if (!ctx_entry) { DRM_DEBUG("out of memory\n"); diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 268a182ae189..42cdb4181643 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -570,9 +570,9 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, struct drm_mode_crtc *crtc_req = data; struct drm_crtc *crtc; struct drm_plane *plane; - struct drm_connector **connector_set, *connector; - struct drm_framebuffer *fb; - struct drm_display_mode *mode; + struct drm_connector **connector_set = NULL, *connector; + struct drm_framebuffer *fb = NULL; + struct drm_display_mode *mode = NULL; struct drm_mode_set set; uint32_t __user *set_connectors_ptr; struct drm_modeset_acquire_ctx ctx; @@ -599,15 +599,8 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, plane = crtc->primary; mutex_lock(&crtc->dev->mode_config.mutex); - drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); -retry: - connector_set = NULL; - fb = NULL; - mode = NULL; - - ret = drm_modeset_lock_all_ctx(crtc->dev, &ctx); - if (ret) - goto out; + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, + DRM_MODESET_ACQUIRE_INTERRUPTIBLE, ret); if (crtc_req->mode_valid) { /* If we have a mode we need a framebuffer. */ @@ -766,13 +759,13 @@ out: } kfree(connector_set); drm_mode_destroy(dev, mode); - if (ret == -EDEADLK) { - ret = drm_modeset_backoff(&ctx); - if (!ret) - goto retry; - } - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); + + /* In case we need to retry... */ + connector_set = NULL; + fb = NULL; + mode = NULL; + + DRM_MODESET_LOCK_ALL_END(ctx, ret); mutex_unlock(&crtc->dev->mode_config.mutex); return ret; diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 373bd4c2b698..f8468eae0503 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -32,6 +32,8 @@ #include <drm/drm_debugfs.h> #include <drm/drm_edid.h> #include <drm/drm_atomic.h> +#include <drm/drm_auth.h> +#include <drm/drm_gem.h> #include <drm/drmP.h> #include "drm_internal.h" @@ -43,6 +45,93 @@ * Initialization, etc. **************************************************/ +static int drm_name_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_minor *minor = node->minor; + struct drm_device *dev = minor->dev; + struct drm_master *master; + + mutex_lock(&dev->master_mutex); + master = dev->master; + seq_printf(m, "%s", dev->driver->name); + if (dev->dev) + seq_printf(m, " dev=%s", dev_name(dev->dev)); + if (master && master->unique) + seq_printf(m, " master=%s", master->unique); + if (dev->unique) + seq_printf(m, " unique=%s", dev->unique); + seq_printf(m, "\n"); + mutex_unlock(&dev->master_mutex); + + return 0; +} + +static int drm_clients_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + struct drm_file *priv; + kuid_t uid; + + seq_printf(m, + "%20s %5s %3s master a %5s %10s\n", + "command", + "pid", + "dev", + "uid", + "magic"); + + /* dev->filelist is sorted youngest first, but we want to present + * oldest first (i.e. kernel, servers, clients), so walk backwardss. + */ + mutex_lock(&dev->filelist_mutex); + list_for_each_entry_reverse(priv, &dev->filelist, lhead) { + struct task_struct *task; + + rcu_read_lock(); /* locks pid_task()->comm */ + task = pid_task(priv->pid, PIDTYPE_PID); + uid = task ? __task_cred(task)->euid : GLOBAL_ROOT_UID; + seq_printf(m, "%20s %5d %3d %c %c %5d %10u\n", + task ? task->comm : "<unknown>", + pid_vnr(priv->pid), + priv->minor->index, + drm_is_current_master(priv) ? 'y' : 'n', + priv->authenticated ? 'y' : 'n', + from_kuid_munged(seq_user_ns(m), uid), + priv->magic); + rcu_read_unlock(); + } + mutex_unlock(&dev->filelist_mutex); + return 0; +} + +static int drm_gem_one_name_info(int id, void *ptr, void *data) +{ + struct drm_gem_object *obj = ptr; + struct seq_file *m = data; + + seq_printf(m, "%6d %8zd %7d %8d\n", + obj->name, obj->size, + obj->handle_count, + kref_read(&obj->refcount)); + return 0; +} + +static int drm_gem_name_info(struct seq_file *m, void *data) +{ + struct drm_info_node *node = (struct drm_info_node *) m->private; + struct drm_device *dev = node->minor->dev; + + seq_printf(m, " name size handles refcount\n"); + + mutex_lock(&dev->object_name_lock); + idr_for_each(&dev->object_name_idr, drm_gem_one_name_info, m); + mutex_unlock(&dev->object_name_lock); + + return 0; +} + static const struct drm_info_list drm_debugfs_list[] = { {"name", drm_name_info, 0}, {"clients", drm_clients_info, 0}, diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 529414556962..2ab16c9e6243 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -33,6 +33,7 @@ #include <drm/drm_fixed.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> /** * DOC: dp mst helper @@ -1639,7 +1640,7 @@ static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr, for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) { drm_dp_add_port(mstb, mgr->dev, &txmsg->reply.u.link_addr.ports[i]); } - (*mgr->cbs->hotplug)(mgr); + drm_kms_helper_hotplug_event(mgr->dev); } } else { mstb->link_address_sent = false; @@ -1878,41 +1879,48 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr) mutex_lock(&mgr->payload_lock); for (i = 0; i < mgr->max_payloads; i++) { + struct drm_dp_vcpi *vcpi = mgr->proposed_vcpis[i]; + struct drm_dp_payload *payload = &mgr->payloads[i]; + /* solve the current payloads - compare to the hw ones - update the hw view */ req_payload.start_slot = cur_slots; - if (mgr->proposed_vcpis[i]) { - port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi); + if (vcpi) { + port = container_of(vcpi, struct drm_dp_mst_port, + vcpi); port = drm_dp_get_validated_port_ref(mgr, port); if (!port) { mutex_unlock(&mgr->payload_lock); return -EINVAL; } - req_payload.num_slots = mgr->proposed_vcpis[i]->num_slots; - req_payload.vcpi = mgr->proposed_vcpis[i]->vcpi; + req_payload.num_slots = vcpi->num_slots; + req_payload.vcpi = vcpi->vcpi; } else { port = NULL; req_payload.num_slots = 0; } - if (mgr->payloads[i].start_slot != req_payload.start_slot) { - mgr->payloads[i].start_slot = req_payload.start_slot; - } + payload->start_slot = req_payload.start_slot; /* work out what is required to happen with this payload */ - if (mgr->payloads[i].num_slots != req_payload.num_slots) { + if (payload->num_slots != req_payload.num_slots) { /* need to push an update for this payload */ if (req_payload.num_slots) { - drm_dp_create_payload_step1(mgr, mgr->proposed_vcpis[i]->vcpi, &req_payload); - mgr->payloads[i].num_slots = req_payload.num_slots; - mgr->payloads[i].vcpi = req_payload.vcpi; - } else if (mgr->payloads[i].num_slots) { - mgr->payloads[i].num_slots = 0; - drm_dp_destroy_payload_step1(mgr, port, mgr->payloads[i].vcpi, &mgr->payloads[i]); - req_payload.payload_state = mgr->payloads[i].payload_state; - mgr->payloads[i].start_slot = 0; + drm_dp_create_payload_step1(mgr, vcpi->vcpi, + &req_payload); + payload->num_slots = req_payload.num_slots; + payload->vcpi = req_payload.vcpi; + + } else if (payload->num_slots) { + payload->num_slots = 0; + drm_dp_destroy_payload_step1(mgr, port, + payload->vcpi, + payload); + req_payload.payload_state = + payload->payload_state; + payload->start_slot = 0; } - mgr->payloads[i].payload_state = req_payload.payload_state; + payload->payload_state = req_payload.payload_state; } cur_slots += req_payload.num_slots; @@ -1921,22 +1929,26 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr) } for (i = 0; i < mgr->max_payloads; i++) { - if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) { - DRM_DEBUG_KMS("removing payload %d\n", i); - for (j = i; j < mgr->max_payloads - 1; j++) { - memcpy(&mgr->payloads[j], &mgr->payloads[j + 1], sizeof(struct drm_dp_payload)); - mgr->proposed_vcpis[j] = mgr->proposed_vcpis[j + 1]; - if (mgr->proposed_vcpis[j] && mgr->proposed_vcpis[j]->num_slots) { - set_bit(j + 1, &mgr->payload_mask); - } else { - clear_bit(j + 1, &mgr->payload_mask); - } - } - memset(&mgr->payloads[mgr->max_payloads - 1], 0, sizeof(struct drm_dp_payload)); - mgr->proposed_vcpis[mgr->max_payloads - 1] = NULL; - clear_bit(mgr->max_payloads, &mgr->payload_mask); + if (mgr->payloads[i].payload_state != DP_PAYLOAD_DELETE_LOCAL) + continue; + + DRM_DEBUG_KMS("removing payload %d\n", i); + for (j = i; j < mgr->max_payloads - 1; j++) { + mgr->payloads[j] = mgr->payloads[j + 1]; + mgr->proposed_vcpis[j] = mgr->proposed_vcpis[j + 1]; + if (mgr->proposed_vcpis[j] && + mgr->proposed_vcpis[j]->num_slots) { + set_bit(j + 1, &mgr->payload_mask); + } else { + clear_bit(j + 1, &mgr->payload_mask); + } } + + memset(&mgr->payloads[mgr->max_payloads - 1], 0, + sizeof(struct drm_dp_payload)); + mgr->proposed_vcpis[mgr->max_payloads - 1] = NULL; + clear_bit(mgr->max_payloads, &mgr->payload_mask); } mutex_unlock(&mgr->payload_lock); @@ -2412,7 +2424,7 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) drm_dp_update_port(mstb, &msg.u.conn_stat); DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type); - (*mgr->cbs->hotplug)(mgr); + drm_kms_helper_hotplug_event(mgr->dev); } else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) { drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false); @@ -3109,7 +3121,7 @@ static void drm_dp_destroy_connector_work(struct work_struct *work) send_hotplug = true; } if (send_hotplug) - (*mgr->cbs->hotplug)(mgr); + drm_kms_helper_hotplug_event(mgr->dev); } static struct drm_private_state * @@ -3220,7 +3232,7 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, /* max. time slots - one slot for MTP header */ mst_state->avail_slots = 63; - drm_atomic_private_obj_init(&mgr->base, + drm_atomic_private_obj_init(dev, &mgr->base, &mst_state->base, &mst_state_funcs); @@ -3234,6 +3246,7 @@ EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init); */ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr) { + drm_dp_mst_topology_mgr_set_mst(mgr, false); flush_work(&mgr->work); flush_work(&mgr->destroy_connector_work); mutex_lock(&mgr->payload_lock); @@ -3249,6 +3262,23 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr) } EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy); +static bool remote_i2c_read_ok(const struct i2c_msg msgs[], int num) +{ + int i; + + if (num - 1 > DP_REMOTE_I2C_READ_MAX_TRANSACTIONS) + return false; + + for (i = 0; i < num - 1; i++) { + if (msgs[i].flags & I2C_M_RD || + msgs[i].len > 0xff) + return false; + } + + return msgs[num - 1].flags & I2C_M_RD && + msgs[num - 1].len <= 0xff; +} + /* I2C device */ static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) @@ -3258,7 +3288,6 @@ static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs struct drm_dp_mst_branch *mstb; struct drm_dp_mst_topology_mgr *mgr = port->mgr; unsigned int i; - bool reading = false; struct drm_dp_sideband_msg_req_body msg; struct drm_dp_sideband_msg_tx *txmsg = NULL; int ret; @@ -3267,12 +3296,7 @@ static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs if (!mstb) return -EREMOTEIO; - /* construct i2c msg */ - /* see if last msg is a read */ - if (msgs[num - 1].flags & I2C_M_RD) - reading = true; - - if (!reading || (num - 1 > DP_REMOTE_I2C_READ_MAX_TRANSACTIONS)) { + if (!remote_i2c_read_ok(msgs, num)) { DRM_DEBUG_KMS("Unsupported I2C transaction for MST device\n"); ret = -EIO; goto out; @@ -3286,6 +3310,7 @@ static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs msg.u.i2c_read.transactions[i].i2c_dev_id = msgs[i].addr; msg.u.i2c_read.transactions[i].num_bytes = msgs[i].len; msg.u.i2c_read.transactions[i].bytes = msgs[i].buf; + msg.u.i2c_read.transactions[i].no_stop_bit = !(msgs[i].flags & I2C_M_STOP); } msg.u.i2c_read.read_i2c_device_id = msgs[num - 1].addr; msg.u.i2c_read.num_bytes_read = msgs[num - 1].len; diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index e2ffecd5e453..a5fe91b8c3c9 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -41,7 +41,6 @@ #include "drm_crtc_internal.h" #include "drm_legacy.h" #include "drm_internal.h" -#include "drm_crtc_internal.h" /* * drm_debug: Enable debug output. @@ -704,19 +703,6 @@ void drm_dev_put(struct drm_device *dev) } EXPORT_SYMBOL(drm_dev_put); -/** - * drm_dev_unref - Drop reference of a DRM device - * @dev: device to drop reference of or NULL - * - * This is a compatibility alias for drm_dev_put() and should not be used by new - * code. - */ -void drm_dev_unref(struct drm_device *dev) -{ - drm_dev_put(dev); -} -EXPORT_SYMBOL(drm_dev_unref); - static int create_compat_control_link(struct drm_device *dev) { struct drm_minor *minor; diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 9a69ad7e9f3b..5e9ca6f96379 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -219,6 +219,9 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) mutex_lock(&fb_helper->lock); drm_connector_list_iter_begin(dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) { + if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) + continue; + ret = __drm_fb_helper_add_one_connector(fb_helper, connector); if (ret) goto fail; diff --git a/drivers/gpu/drm/drm_fourcc.c b/drivers/gpu/drm/drm_fourcc.c index f523948c82b1..d90ee03a84c6 100644 --- a/drivers/gpu/drm/drm_fourcc.c +++ b/drivers/gpu/drm/drm_fourcc.c @@ -224,6 +224,7 @@ const struct drm_format_info *__drm_format_info(u32 format) { .format = DRM_FORMAT_YVYU, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true }, { .format = DRM_FORMAT_UYVY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true }, { .format = DRM_FORMAT_VYUY, .depth = 0, .num_planes = 1, .cpp = { 2, 0, 0 }, .hsub = 2, .vsub = 1, .is_yuv = true }, + { .format = DRM_FORMAT_XYUV8888, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .is_yuv = true }, { .format = DRM_FORMAT_AYUV, .depth = 0, .num_planes = 1, .cpp = { 4, 0, 0 }, .hsub = 1, .vsub = 1, .has_alpha = true, .is_yuv = true }, { .format = DRM_FORMAT_Y0L0, .depth = 0, .num_planes = 1, .char_per_block = { 8, 0, 0 }, .block_w = { 2, 0, 0 }, .block_h = { 2, 0, 0 }, diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 512078ebd97b..8b55ece97967 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -257,7 +257,9 @@ drm_gem_object_release_handle(int id, void *ptr, void *data) struct drm_gem_object *obj = ptr; struct drm_device *dev = obj->dev; - if (dev->driver->gem_close_object) + if (obj->funcs && obj->funcs->close) + obj->funcs->close(obj, file_priv); + else if (dev->driver->gem_close_object) dev->driver->gem_close_object(obj, file_priv); if (drm_core_check_feature(dev, DRIVER_PRIME)) @@ -410,7 +412,11 @@ drm_gem_handle_create_tail(struct drm_file *file_priv, if (ret) goto err_remove; - if (dev->driver->gem_open_object) { + if (obj->funcs && obj->funcs->open) { + ret = obj->funcs->open(obj, file_priv); + if (ret) + goto err_revoke; + } else if (dev->driver->gem_open_object) { ret = dev->driver->gem_open_object(obj, file_priv); if (ret) goto err_revoke; @@ -835,7 +841,9 @@ drm_gem_object_free(struct kref *kref) container_of(kref, struct drm_gem_object, refcount); struct drm_device *dev = obj->dev; - if (dev->driver->gem_free_object_unlocked) { + if (obj->funcs) { + obj->funcs->free(obj); + } else if (dev->driver->gem_free_object_unlocked) { dev->driver->gem_free_object_unlocked(obj); } else if (dev->driver->gem_free_object) { WARN_ON(!mutex_is_locked(&dev->struct_mutex)); @@ -864,13 +872,13 @@ drm_gem_object_put_unlocked(struct drm_gem_object *obj) dev = obj->dev; - if (dev->driver->gem_free_object_unlocked) { - kref_put(&obj->refcount, drm_gem_object_free); - } else { + if (dev->driver->gem_free_object) { might_lock(&dev->struct_mutex); if (kref_put_mutex(&obj->refcount, drm_gem_object_free, &dev->struct_mutex)) mutex_unlock(&dev->struct_mutex); + } else { + kref_put(&obj->refcount, drm_gem_object_free); } } EXPORT_SYMBOL(drm_gem_object_put_unlocked); @@ -960,11 +968,14 @@ int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, if (obj_size < vma->vm_end - vma->vm_start) return -EINVAL; - if (!dev->driver->gem_vm_ops) + if (obj->funcs && obj->funcs->vm_ops) + vma->vm_ops = obj->funcs->vm_ops; + else if (dev->driver->gem_vm_ops) + vma->vm_ops = dev->driver->gem_vm_ops; + else return -EINVAL; vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP; - vma->vm_ops = dev->driver->gem_vm_ops; vma->vm_private_data = obj; vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); @@ -1066,6 +1077,86 @@ void drm_gem_print_info(struct drm_printer *p, unsigned int indent, drm_printf_indent(p, indent, "imported=%s\n", obj->import_attach ? "yes" : "no"); - if (obj->dev->driver->gem_print_info) + if (obj->funcs && obj->funcs->print_info) + obj->funcs->print_info(p, indent, obj); + else if (obj->dev->driver->gem_print_info) obj->dev->driver->gem_print_info(p, indent, obj); } + +/** + * drm_gem_pin - Pin backing buffer in memory + * @obj: GEM object + * + * Make sure the backing buffer is pinned in memory. + * + * Returns: + * 0 on success or a negative error code on failure. + */ +int drm_gem_pin(struct drm_gem_object *obj) +{ + if (obj->funcs && obj->funcs->pin) + return obj->funcs->pin(obj); + else if (obj->dev->driver->gem_prime_pin) + return obj->dev->driver->gem_prime_pin(obj); + else + return 0; +} +EXPORT_SYMBOL(drm_gem_pin); + +/** + * drm_gem_unpin - Unpin backing buffer from memory + * @obj: GEM object + * + * Relax the requirement that the backing buffer is pinned in memory. + */ +void drm_gem_unpin(struct drm_gem_object *obj) +{ + if (obj->funcs && obj->funcs->unpin) + obj->funcs->unpin(obj); + else if (obj->dev->driver->gem_prime_unpin) + obj->dev->driver->gem_prime_unpin(obj); +} +EXPORT_SYMBOL(drm_gem_unpin); + +/** + * drm_gem_vmap - Map buffer into kernel virtual address space + * @obj: GEM object + * + * Returns: + * A virtual pointer to a newly created GEM object or an ERR_PTR-encoded negative + * error code on failure. + */ +void *drm_gem_vmap(struct drm_gem_object *obj) +{ + void *vaddr; + + if (obj->funcs && obj->funcs->vmap) + vaddr = obj->funcs->vmap(obj); + else if (obj->dev->driver->gem_prime_vmap) + vaddr = obj->dev->driver->gem_prime_vmap(obj); + else + vaddr = ERR_PTR(-EOPNOTSUPP); + + if (!vaddr) + vaddr = ERR_PTR(-ENOMEM); + + return vaddr; +} +EXPORT_SYMBOL(drm_gem_vmap); + +/** + * drm_gem_vunmap - Remove buffer mapping from kernel virtual address space + * @obj: GEM object + * @vaddr: Virtual address (can be NULL) + */ +void drm_gem_vunmap(struct drm_gem_object *obj, void *vaddr) +{ + if (!vaddr) + return; + + if (obj->funcs && obj->funcs->vunmap) + obj->funcs->vunmap(obj, vaddr); + else if (obj->dev->driver->gem_prime_vunmap) + obj->dev->driver->gem_prime_vunmap(obj, vaddr); +} +EXPORT_SYMBOL(drm_gem_vunmap); diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 1d2ced882b66..cc26625b4b33 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -176,6 +176,7 @@ drm_gem_cma_create_with_handle(struct drm_file *file_priv, * * This function frees the backing memory of the CMA GEM object, cleans up the * GEM object state and frees the memory used to store the object itself. + * If the buffer is imported and the virtual address is set, it is released. * Drivers using the CMA helpers should set this as their * &drm_driver.gem_free_object_unlocked callback. */ @@ -189,6 +190,8 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj) dma_free_wc(gem_obj->dev->dev, cma_obj->base.size, cma_obj->vaddr, cma_obj->paddr); } else if (gem_obj->import_attach) { + if (cma_obj->vaddr) + dma_buf_vunmap(gem_obj->import_attach->dmabuf, cma_obj->vaddr); drm_prime_gem_destroy(gem_obj, cma_obj->sgt); } @@ -575,3 +578,86 @@ void drm_gem_cma_prime_vunmap(struct drm_gem_object *obj, void *vaddr) /* Nothing to do */ } EXPORT_SYMBOL_GPL(drm_gem_cma_prime_vunmap); + +static const struct drm_gem_object_funcs drm_cma_gem_default_funcs = { + .free = drm_gem_cma_free_object, + .print_info = drm_gem_cma_print_info, + .get_sg_table = drm_gem_cma_prime_get_sg_table, + .vmap = drm_gem_cma_prime_vmap, + .vm_ops = &drm_gem_cma_vm_ops, +}; + +/** + * drm_cma_gem_create_object_default_funcs - Create a CMA GEM object with a + * default function table + * @dev: DRM device + * @size: Size of the object to allocate + * + * This sets the GEM object functions to the default CMA helper functions. + * This function can be used as the &drm_driver.gem_create_object callback. + * + * Returns: + * A pointer to a allocated GEM object or an error pointer on failure. + */ +struct drm_gem_object * +drm_cma_gem_create_object_default_funcs(struct drm_device *dev, size_t size) +{ + struct drm_gem_cma_object *cma_obj; + + cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL); + if (!cma_obj) + return NULL; + + cma_obj->base.funcs = &drm_cma_gem_default_funcs; + + return &cma_obj->base; +} +EXPORT_SYMBOL(drm_cma_gem_create_object_default_funcs); + +/** + * drm_gem_cma_prime_import_sg_table_vmap - PRIME import another driver's + * scatter/gather table and get the virtual address of the buffer + * @dev: DRM device + * @attach: DMA-BUF attachment + * @sgt: Scatter/gather table of pinned pages + * + * This function imports a scatter/gather table using + * drm_gem_cma_prime_import_sg_table() and uses dma_buf_vmap() to get the kernel + * virtual address. This ensures that a CMA GEM object always has its virtual + * address set. This address is released when the object is freed. + * + * This function can be used as the &drm_driver.gem_prime_import_sg_table + * callback. The DRM_GEM_CMA_VMAP_DRIVER_OPS() macro provides a shortcut to set + * the necessary DRM driver operations. + * + * Returns: + * A pointer to a newly created GEM object or an ERR_PTR-encoded negative + * error code on failure. + */ +struct drm_gem_object * +drm_gem_cma_prime_import_sg_table_vmap(struct drm_device *dev, + struct dma_buf_attachment *attach, + struct sg_table *sgt) +{ + struct drm_gem_cma_object *cma_obj; + struct drm_gem_object *obj; + void *vaddr; + + vaddr = dma_buf_vmap(attach->dmabuf); + if (!vaddr) { + DRM_ERROR("Failed to vmap PRIME buffer\n"); + return ERR_PTR(-ENOMEM); + } + + obj = drm_gem_cma_prime_import_sg_table(dev, attach, sgt); + if (IS_ERR(obj)) { + dma_buf_vunmap(attach->dmabuf, vaddr); + return obj; + } + + cma_obj = to_drm_gem_cma_obj(obj); + cma_obj->vaddr = vaddr; + + return obj; +} +EXPORT_SYMBOL(drm_gem_cma_prime_import_sg_table_vmap); diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c deleted file mode 100644 index 6b68e9088436..000000000000 --- a/drivers/gpu/drm/drm_info.c +++ /dev/null @@ -1,137 +0,0 @@ -/** - * \file drm_info.c - * DRM info file implementations - * - * \author Ben Gamari <bgamari@gmail.com> - */ - -/* - * Created: Sun Dec 21 13:09:50 2008 by bgamari@gmail.com - * - * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. - * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. - * Copyright 2008 Ben Gamari <bgamari@gmail.com> - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * 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 - * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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/seq_file.h> -#include <drm/drmP.h> -#include <drm/drm_gem.h> - -#include "drm_internal.h" -#include "drm_legacy.h" - -/** - * Called when "/proc/dri/.../name" is read. - * - * Prints the device name together with the bus id if available. - */ -int drm_name_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_minor *minor = node->minor; - struct drm_device *dev = minor->dev; - struct drm_master *master; - - mutex_lock(&dev->master_mutex); - master = dev->master; - seq_printf(m, "%s", dev->driver->name); - if (dev->dev) - seq_printf(m, " dev=%s", dev_name(dev->dev)); - if (master && master->unique) - seq_printf(m, " master=%s", master->unique); - if (dev->unique) - seq_printf(m, " unique=%s", dev->unique); - seq_printf(m, "\n"); - mutex_unlock(&dev->master_mutex); - - return 0; -} - -/** - * Called when "/proc/dri/.../clients" is read. - * - */ -int drm_clients_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - struct drm_file *priv; - kuid_t uid; - - seq_printf(m, - "%20s %5s %3s master a %5s %10s\n", - "command", - "pid", - "dev", - "uid", - "magic"); - - /* dev->filelist is sorted youngest first, but we want to present - * oldest first (i.e. kernel, servers, clients), so walk backwardss. - */ - mutex_lock(&dev->filelist_mutex); - list_for_each_entry_reverse(priv, &dev->filelist, lhead) { - struct task_struct *task; - - rcu_read_lock(); /* locks pid_task()->comm */ - task = pid_task(priv->pid, PIDTYPE_PID); - uid = task ? __task_cred(task)->euid : GLOBAL_ROOT_UID; - seq_printf(m, "%20s %5d %3d %c %c %5d %10u\n", - task ? task->comm : "<unknown>", - pid_vnr(priv->pid), - priv->minor->index, - drm_is_current_master(priv) ? 'y' : 'n', - priv->authenticated ? 'y' : 'n', - from_kuid_munged(seq_user_ns(m), uid), - priv->magic); - rcu_read_unlock(); - } - mutex_unlock(&dev->filelist_mutex); - return 0; -} - -static int drm_gem_one_name_info(int id, void *ptr, void *data) -{ - struct drm_gem_object *obj = ptr; - struct seq_file *m = data; - - seq_printf(m, "%6d %8zd %7d %8d\n", - obj->name, obj->size, - obj->handle_count, - kref_read(&obj->refcount)); - return 0; -} - -int drm_gem_name_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_device *dev = node->minor->dev; - - seq_printf(m, " name size handles refcount\n"); - - mutex_lock(&dev->object_name_lock); - idr_for_each(&dev->object_name_idr, drm_gem_one_name_info, m); - mutex_unlock(&dev->object_name_lock); - - return 0; -} diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 0c4eb4a9ab31..331112b2ae88 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -26,6 +26,8 @@ #define DRM_IF_MAJOR 1 #define DRM_IF_MINOR 4 +#define DRM_IF_VERSION(maj, min) (maj << 16 | min) + struct drm_prime_file_private; struct dma_buf; @@ -56,11 +58,6 @@ void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpr struct drm_minor *drm_minor_acquire(unsigned int minor_id); void drm_minor_release(struct drm_minor *minor); -/* drm_info.c */ -int drm_name_info(struct seq_file *m, void *data); -int drm_clients_info(struct seq_file *m, void* data); -int drm_gem_name_info(struct seq_file *m, void *data); - /* drm_vblank.c */ void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe); void drm_vblank_cleanup(struct drm_device *dev); diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c index 3650d3c46718..d9b4d3ff06f6 100644 --- a/drivers/gpu/drm/drm_lease.c +++ b/drivers/gpu/drm/drm_lease.c @@ -218,7 +218,7 @@ static struct drm_master *drm_lease_create(struct drm_master *lessor, struct idr idr_for_each_entry(leases, entry, object) { error = 0; - if (!idr_find(&dev->mode_config.crtc_idr, object)) + if (!idr_find(&dev->mode_config.object_idr, object)) error = -ENOENT; else if (!_drm_lease_held_master(lessor, object)) error = -EACCES; @@ -439,7 +439,7 @@ static int fill_object_idr(struct drm_device *dev, /* * We're using an IDR to hold the set of leased * objects, but we don't need to point at the object's - * data structure from the lease as the main crtc_idr + * data structure from the lease as the main object_idr * will be used to actually find that. Instead, all we * really want is a 'leased/not-leased' result, for * which any non-NULL pointer will work fine. @@ -687,7 +687,7 @@ int drm_mode_get_lease_ioctl(struct drm_device *dev, if (lessee->lessor == NULL) /* owner can use all objects */ - object_idr = &lessee->dev->mode_config.crtc_idr; + object_idr = &lessee->dev->mode_config.object_idr; else /* lessee can only use allowed object */ object_idr = &lessee->leases; diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index ee80788f2c40..037e243ec863 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -381,7 +381,8 @@ void drm_mode_config_init(struct drm_device *dev) INIT_LIST_HEAD(&dev->mode_config.property_list); INIT_LIST_HEAD(&dev->mode_config.property_blob_list); INIT_LIST_HEAD(&dev->mode_config.plane_list); - idr_init(&dev->mode_config.crtc_idr); + INIT_LIST_HEAD(&dev->mode_config.privobj_list); + idr_init(&dev->mode_config.object_idr); idr_init(&dev->mode_config.tile_idr); ida_init(&dev->mode_config.connector_ida); spin_lock_init(&dev->mode_config.connector_list_lock); @@ -484,7 +485,7 @@ void drm_mode_config_cleanup(struct drm_device *dev) ida_destroy(&dev->mode_config.connector_ida); idr_destroy(&dev->mode_config.tile_idr); - idr_destroy(&dev->mode_config.crtc_idr); + idr_destroy(&dev->mode_config.object_idr); drm_modeset_lock_fini(&dev->mode_config.connection_mutex); } EXPORT_SYMBOL(drm_mode_config_cleanup); diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c index cd9bc0ce9be0..a9005c1c2384 100644 --- a/drivers/gpu/drm/drm_mode_object.c +++ b/drivers/gpu/drm/drm_mode_object.c @@ -38,7 +38,7 @@ int __drm_mode_object_add(struct drm_device *dev, struct drm_mode_object *obj, int ret; mutex_lock(&dev->mode_config.idr_mutex); - ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, + ret = idr_alloc(&dev->mode_config.object_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL); if (ret >= 0) { /* @@ -79,7 +79,7 @@ void drm_mode_object_register(struct drm_device *dev, struct drm_mode_object *obj) { mutex_lock(&dev->mode_config.idr_mutex); - idr_replace(&dev->mode_config.crtc_idr, obj, obj->id); + idr_replace(&dev->mode_config.object_idr, obj, obj->id); mutex_unlock(&dev->mode_config.idr_mutex); } @@ -99,7 +99,7 @@ void drm_mode_object_unregister(struct drm_device *dev, { mutex_lock(&dev->mode_config.idr_mutex); if (object->id) { - idr_remove(&dev->mode_config.crtc_idr, object->id); + idr_remove(&dev->mode_config.object_idr, object->id); object->id = 0; } mutex_unlock(&dev->mode_config.idr_mutex); @@ -131,7 +131,7 @@ struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, struct drm_mode_object *obj = NULL; mutex_lock(&dev->mode_config.idr_mutex); - obj = idr_find(&dev->mode_config.crtc_idr, id); + obj = idr_find(&dev->mode_config.object_idr, id); if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type) obj = NULL; if (obj && obj->id != id) @@ -459,12 +459,13 @@ static int set_property_atomic(struct drm_mode_object *obj, struct drm_modeset_acquire_ctx ctx; int ret; - drm_modeset_acquire_init(&ctx, 0); - state = drm_atomic_state_alloc(dev); if (!state) return -ENOMEM; + + drm_modeset_acquire_init(&ctx, 0); state->acquire_ctx = &ctx; + retry: if (prop == state->dev->mode_config.dpms_property) { if (obj->type != DRM_MODE_OBJECT_CONNECTOR) { diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index 8a5100685875..81dd11901ffd 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -22,6 +22,7 @@ */ #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_crtc.h> #include <drm/drm_modeset_lock.h> @@ -56,6 +57,10 @@ * drm_modeset_drop_locks(ctx); * drm_modeset_acquire_fini(ctx); * + * For convenience this control flow is implemented in + * DRM_MODESET_LOCK_ALL_BEGIN() and DRM_MODESET_LOCK_ALL_END() for the case + * where all modeset locks need to be taken through drm_modeset_lock_all_ctx(). + * * If all that is needed is a single modeset lock, then the &struct * drm_modeset_acquire_ctx is not needed and the locking can be simplified * by passing a NULL instead of ctx in the drm_modeset_lock() call or @@ -383,11 +388,14 @@ EXPORT_SYMBOL(drm_modeset_unlock); * Locks acquired with this function should be released by calling the * drm_modeset_drop_locks() function on @ctx. * + * See also: DRM_MODESET_LOCK_ALL_BEGIN() and DRM_MODESET_LOCK_ALL_END() + * * Returns: 0 on success or a negative error-code on failure. */ int drm_modeset_lock_all_ctx(struct drm_device *dev, struct drm_modeset_acquire_ctx *ctx) { + struct drm_private_obj *privobj; struct drm_crtc *crtc; struct drm_plane *plane; int ret; @@ -408,6 +416,12 @@ int drm_modeset_lock_all_ctx(struct drm_device *dev, return ret; } + drm_for_each_privobj(privobj, dev) { + ret = drm_modeset_lock(&privobj->lock, ctx); + if (ret) + return ret; + } + return 0; } EXPORT_SYMBOL(drm_modeset_lock_all_ctx); diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index ab4e70e63f6e..52e445bb1aa5 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -63,7 +63,7 @@ static const struct drm_dmi_panel_orientation_data gpd_win2 = { .width = 720, .height = 1280, .bios_dates = (const char * const []){ - "12/07/2017", "05/24/2018", NULL }, + "12/07/2017", "05/24/2018", "06/29/2018", NULL }, .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, }; diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 679455e36829..5f650d8fc66b 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -767,11 +767,8 @@ static int setplane_internal(struct drm_plane *plane, struct drm_modeset_acquire_ctx ctx; int ret; - drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); -retry: - ret = drm_modeset_lock_all_ctx(plane->dev, &ctx); - if (ret) - goto fail; + DRM_MODESET_LOCK_ALL_BEGIN(plane->dev, ctx, + DRM_MODESET_ACQUIRE_INTERRUPTIBLE, ret); if (drm_drv_uses_atomic_modeset(plane->dev)) ret = __setplane_atomic(plane, crtc, fb, @@ -782,14 +779,7 @@ retry: crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h, &ctx); -fail: - if (ret == -EDEADLK) { - ret = drm_modeset_backoff(&ctx); - if (!ret) - goto retry; - } - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); + DRM_MODESET_LOCK_ALL_END(ctx, ret); return ret; } diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 8d54d51a6b6b..231e3f6d5f41 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -199,7 +199,6 @@ int drm_gem_map_attach(struct dma_buf *dma_buf, { struct drm_prime_attachment *prime_attach; struct drm_gem_object *obj = dma_buf->priv; - struct drm_device *dev = obj->dev; prime_attach = kzalloc(sizeof(*prime_attach), GFP_KERNEL); if (!prime_attach) @@ -208,10 +207,7 @@ int drm_gem_map_attach(struct dma_buf *dma_buf, prime_attach->dir = DMA_NONE; attach->priv = prime_attach; - if (!dev->driver->gem_prime_pin) - return 0; - - return dev->driver->gem_prime_pin(obj); + return drm_gem_pin(obj); } EXPORT_SYMBOL(drm_gem_map_attach); @@ -228,7 +224,6 @@ void drm_gem_map_detach(struct dma_buf *dma_buf, { struct drm_prime_attachment *prime_attach = attach->priv; struct drm_gem_object *obj = dma_buf->priv; - struct drm_device *dev = obj->dev; if (prime_attach) { struct sg_table *sgt = prime_attach->sgt; @@ -247,8 +242,7 @@ void drm_gem_map_detach(struct dma_buf *dma_buf, attach->priv = NULL; } - if (dev->driver->gem_prime_unpin) - dev->driver->gem_prime_unpin(obj); + drm_gem_unpin(obj); } EXPORT_SYMBOL(drm_gem_map_detach); @@ -310,7 +304,10 @@ struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach, if (WARN_ON(prime_attach->dir != DMA_NONE)) return ERR_PTR(-EBUSY); - sgt = obj->dev->driver->gem_prime_get_sg_table(obj); + if (obj->funcs) + sgt = obj->funcs->get_sg_table(obj); + else + sgt = obj->dev->driver->gem_prime_get_sg_table(obj); if (!IS_ERR(sgt)) { if (!dma_map_sg_attrs(attach->dev, sgt->sgl, sgt->nents, dir, @@ -406,12 +403,13 @@ EXPORT_SYMBOL(drm_gem_dmabuf_release); void *drm_gem_dmabuf_vmap(struct dma_buf *dma_buf) { struct drm_gem_object *obj = dma_buf->priv; - struct drm_device *dev = obj->dev; + void *vaddr; - if (dev->driver->gem_prime_vmap) - return dev->driver->gem_prime_vmap(obj); - else - return NULL; + vaddr = drm_gem_vmap(obj); + if (IS_ERR(vaddr)) + vaddr = NULL; + + return vaddr; } EXPORT_SYMBOL(drm_gem_dmabuf_vmap); @@ -426,10 +424,8 @@ EXPORT_SYMBOL(drm_gem_dmabuf_vmap); void drm_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr) { struct drm_gem_object *obj = dma_buf->priv; - struct drm_device *dev = obj->dev; - if (dev->driver->gem_prime_vunmap) - dev->driver->gem_prime_vunmap(obj, vaddr); + drm_gem_vunmap(obj, vaddr); } EXPORT_SYMBOL(drm_gem_dmabuf_vunmap); @@ -529,7 +525,12 @@ static struct dma_buf *export_and_register_object(struct drm_device *dev, return dmabuf; } - dmabuf = dev->driver->gem_prime_export(dev, obj, flags); + if (obj->funcs && obj->funcs->export) + dmabuf = obj->funcs->export(obj, flags); + else if (dev->driver->gem_prime_export) + dmabuf = dev->driver->gem_prime_export(dev, obj, flags); + else + dmabuf = drm_gem_prime_export(dev, obj, flags); if (IS_ERR(dmabuf)) { /* normally the created dma-buf takes ownership of the ref, * but if that fails then drop the ref @@ -649,6 +650,52 @@ out_unlock: EXPORT_SYMBOL(drm_gem_prime_handle_to_fd); /** + * drm_gem_prime_mmap - PRIME mmap function for GEM drivers + * @obj: GEM object + * @vma: Virtual address range + * + * This function sets up a userspace mapping for PRIME exported buffers using + * the same codepath that is used for regular GEM buffer mapping on the DRM fd. + * The fake GEM offset is added to vma->vm_pgoff and &drm_driver->fops->mmap is + * called to set up the mapping. + * + * Drivers can use this as their &drm_driver.gem_prime_mmap callback. + */ +int drm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) +{ + struct drm_file *priv; + struct file *fil; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + fil = kzalloc(sizeof(*fil), GFP_KERNEL); + if (!priv || !fil) { + ret = -ENOMEM; + goto out; + } + + /* Used by drm_gem_mmap() to lookup the GEM object */ + priv->minor = obj->dev->primary; + fil->private_data = priv; + + ret = drm_vma_node_allow(&obj->vma_node, priv); + if (ret) + goto out; + + vma->vm_pgoff += drm_vma_node_start(&obj->vma_node); + + ret = obj->dev->driver->fops->mmap(fil, vma); + + drm_vma_node_revoke(&obj->vma_node, priv); +out: + kfree(priv); + kfree(fil); + + return ret; +} +EXPORT_SYMBOL(drm_gem_prime_mmap); + +/** * drm_gem_prime_import_dev - core implementation of the import callback * @dev: drm_device to import into * @dma_buf: dma-buf object to import @@ -762,7 +809,10 @@ int drm_gem_prime_fd_to_handle(struct drm_device *dev, /* never seen this one, need to import */ mutex_lock(&dev->object_name_lock); - obj = dev->driver->gem_prime_import(dev, dma_buf); + if (dev->driver->gem_prime_import) + obj = dev->driver->gem_prime_import(dev, dma_buf); + else + obj = drm_gem_prime_import(dev, dma_buf); if (IS_ERR(obj)) { ret = PTR_ERR(obj); goto out_unlock; diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index da8175d9c6ff..e19525af0cce 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -56,47 +56,16 @@ #include "drm_internal.h" #include <drm/drm_syncobj.h> -/* merge normal syncobj to timeline syncobj, the point interval is 1 */ -#define DRM_SYNCOBJ_BINARY_POINT 1 - -struct drm_syncobj_stub_fence { - struct dma_fence base; - spinlock_t lock; -}; - -static const char *drm_syncobj_stub_fence_get_name(struct dma_fence *fence) -{ - return "syncobjstub"; -} - -static const struct dma_fence_ops drm_syncobj_stub_fence_ops = { - .get_driver_name = drm_syncobj_stub_fence_get_name, - .get_timeline_name = drm_syncobj_stub_fence_get_name, -}; - -struct drm_syncobj_signal_pt { - struct dma_fence_array *fence_array; - u64 value; - struct list_head list; +struct syncobj_wait_entry { + struct list_head node; + struct task_struct *task; + struct dma_fence *fence; + struct dma_fence_cb fence_cb; }; -static DEFINE_SPINLOCK(signaled_fence_lock); -static struct dma_fence signaled_fence; - -static struct dma_fence *drm_syncobj_get_stub_fence(void) -{ - spin_lock(&signaled_fence_lock); - if (!signaled_fence.ops) { - dma_fence_init(&signaled_fence, - &drm_syncobj_stub_fence_ops, - &signaled_fence_lock, - 0, 0); - dma_fence_signal_locked(&signaled_fence); - } - spin_unlock(&signaled_fence_lock); +static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj, + struct syncobj_wait_entry *wait); - return dma_fence_get(&signaled_fence); -} /** * drm_syncobj_find - lookup and reference a sync object. * @file_private: drm file private pointer @@ -123,302 +92,84 @@ struct drm_syncobj *drm_syncobj_find(struct drm_file *file_private, } EXPORT_SYMBOL(drm_syncobj_find); -static struct dma_fence * -drm_syncobj_find_signal_pt_for_point(struct drm_syncobj *syncobj, - uint64_t point) +static void drm_syncobj_fence_add_wait(struct drm_syncobj *syncobj, + struct syncobj_wait_entry *wait) { - struct drm_syncobj_signal_pt *signal_pt; - - if ((syncobj->type == DRM_SYNCOBJ_TYPE_TIMELINE) && - (point <= syncobj->timeline)) - return drm_syncobj_get_stub_fence(); - - list_for_each_entry(signal_pt, &syncobj->signal_pt_list, list) { - if (point > signal_pt->value) - continue; - if ((syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) && - (point != signal_pt->value)) - continue; - return dma_fence_get(&signal_pt->fence_array->base); - } - return NULL; -} - -static void drm_syncobj_add_callback_locked(struct drm_syncobj *syncobj, - struct drm_syncobj_cb *cb, - drm_syncobj_func_t func) -{ - cb->func = func; - list_add_tail(&cb->node, &syncobj->cb_list); -} - -static void drm_syncobj_fence_get_or_add_callback(struct drm_syncobj *syncobj, - struct dma_fence **fence, - struct drm_syncobj_cb *cb, - drm_syncobj_func_t func) -{ - u64 pt_value = 0; - - WARN_ON(*fence); - - if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) { - /*BINARY syncobj always wait on last pt */ - pt_value = syncobj->signal_point; - - if (pt_value == 0) - pt_value += DRM_SYNCOBJ_BINARY_POINT; - } - - mutex_lock(&syncobj->cb_mutex); - spin_lock(&syncobj->pt_lock); - *fence = drm_syncobj_find_signal_pt_for_point(syncobj, pt_value); - spin_unlock(&syncobj->pt_lock); - if (!*fence) - drm_syncobj_add_callback_locked(syncobj, cb, func); - mutex_unlock(&syncobj->cb_mutex); -} - -static void drm_syncobj_remove_callback(struct drm_syncobj *syncobj, - struct drm_syncobj_cb *cb) -{ - mutex_lock(&syncobj->cb_mutex); - list_del_init(&cb->node); - mutex_unlock(&syncobj->cb_mutex); -} + if (wait->fence) + return; -static void drm_syncobj_init(struct drm_syncobj *syncobj) -{ - spin_lock(&syncobj->pt_lock); - syncobj->timeline_context = dma_fence_context_alloc(1); - syncobj->timeline = 0; - syncobj->signal_point = 0; - init_waitqueue_head(&syncobj->wq); - - INIT_LIST_HEAD(&syncobj->signal_pt_list); - spin_unlock(&syncobj->pt_lock); -} - -static void drm_syncobj_fini(struct drm_syncobj *syncobj) -{ - struct drm_syncobj_signal_pt *signal_pt = NULL, *tmp; - - spin_lock(&syncobj->pt_lock); - list_for_each_entry_safe(signal_pt, tmp, - &syncobj->signal_pt_list, list) { - list_del(&signal_pt->list); - dma_fence_put(&signal_pt->fence_array->base); - kfree(signal_pt); - } - spin_unlock(&syncobj->pt_lock); + spin_lock(&syncobj->lock); + /* We've already tried once to get a fence and failed. Now that we + * have the lock, try one more time just to be sure we don't add a + * callback when a fence has already been set. + */ + if (syncobj->fence) + wait->fence = dma_fence_get( + rcu_dereference_protected(syncobj->fence, 1)); + else + list_add_tail(&wait->node, &syncobj->cb_list); + spin_unlock(&syncobj->lock); } -static int drm_syncobj_create_signal_pt(struct drm_syncobj *syncobj, - struct dma_fence *fence, - u64 point) +static void drm_syncobj_remove_wait(struct drm_syncobj *syncobj, + struct syncobj_wait_entry *wait) { - struct drm_syncobj_signal_pt *signal_pt = - kzalloc(sizeof(struct drm_syncobj_signal_pt), GFP_KERNEL); - struct drm_syncobj_signal_pt *tail_pt; - struct dma_fence **fences; - int num_fences = 0; - int ret = 0, i; - - if (!signal_pt) - return -ENOMEM; - if (!fence) - goto out; - - fences = kmalloc_array(sizeof(void *), 2, GFP_KERNEL); - if (!fences) { - ret = -ENOMEM; - goto out; - } - fences[num_fences++] = dma_fence_get(fence); - /* timeline syncobj must take this dependency */ - if (syncobj->type == DRM_SYNCOBJ_TYPE_TIMELINE) { - spin_lock(&syncobj->pt_lock); - if (!list_empty(&syncobj->signal_pt_list)) { - tail_pt = list_last_entry(&syncobj->signal_pt_list, - struct drm_syncobj_signal_pt, list); - fences[num_fences++] = - dma_fence_get(&tail_pt->fence_array->base); - } - spin_unlock(&syncobj->pt_lock); - } - signal_pt->fence_array = dma_fence_array_create(num_fences, fences, - syncobj->timeline_context, - point, false); - if (!signal_pt->fence_array) { - ret = -ENOMEM; - goto fail; - } + if (!wait->node.next) + return; - spin_lock(&syncobj->pt_lock); - if (syncobj->signal_point >= point) { - DRM_WARN("A later signal is ready!"); - spin_unlock(&syncobj->pt_lock); - goto exist; - } - signal_pt->value = point; - list_add_tail(&signal_pt->list, &syncobj->signal_pt_list); - syncobj->signal_point = point; - spin_unlock(&syncobj->pt_lock); - wake_up_all(&syncobj->wq); - - return 0; -exist: - dma_fence_put(&signal_pt->fence_array->base); -fail: - for (i = 0; i < num_fences; i++) - dma_fence_put(fences[i]); - kfree(fences); -out: - kfree(signal_pt); - return ret; + spin_lock(&syncobj->lock); + list_del_init(&wait->node); + spin_unlock(&syncobj->lock); } -static void drm_syncobj_garbage_collection(struct drm_syncobj *syncobj) -{ - struct drm_syncobj_signal_pt *signal_pt, *tmp, *tail_pt; - - spin_lock(&syncobj->pt_lock); - tail_pt = list_last_entry(&syncobj->signal_pt_list, - struct drm_syncobj_signal_pt, - list); - list_for_each_entry_safe(signal_pt, tmp, - &syncobj->signal_pt_list, list) { - if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY && - signal_pt == tail_pt) - continue; - if (dma_fence_is_signaled(&signal_pt->fence_array->base)) { - syncobj->timeline = signal_pt->value; - list_del(&signal_pt->list); - dma_fence_put(&signal_pt->fence_array->base); - kfree(signal_pt); - } else { - /*signal_pt is in order in list, from small to big, so - * the later must not be signal either */ - break; - } - } - - spin_unlock(&syncobj->pt_lock); -} /** * drm_syncobj_replace_fence - replace fence in a sync object. * @syncobj: Sync object to replace fence in - * @point: timeline point * @fence: fence to install in sync file. * - * This replaces the fence on a sync object, or a timeline point fence. + * This replaces the fence on a sync object. */ void drm_syncobj_replace_fence(struct drm_syncobj *syncobj, - u64 point, struct dma_fence *fence) { - u64 pt_value = point; - - drm_syncobj_garbage_collection(syncobj); - if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) { - if (!fence) { - drm_syncobj_fini(syncobj); - drm_syncobj_init(syncobj); - return; - } - pt_value = syncobj->signal_point + - DRM_SYNCOBJ_BINARY_POINT; - } - drm_syncobj_create_signal_pt(syncobj, fence, pt_value); - if (fence) { - struct drm_syncobj_cb *cur, *tmp; - LIST_HEAD(cb_list); + struct dma_fence *old_fence; + struct syncobj_wait_entry *cur, *tmp; + + if (fence) + dma_fence_get(fence); + + spin_lock(&syncobj->lock); + + old_fence = rcu_dereference_protected(syncobj->fence, + lockdep_is_held(&syncobj->lock)); + rcu_assign_pointer(syncobj->fence, fence); - mutex_lock(&syncobj->cb_mutex); + if (fence != old_fence) { list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node) { list_del_init(&cur->node); - cur->func(syncobj, cur); + syncobj_wait_syncobj_func(syncobj, cur); } - mutex_unlock(&syncobj->cb_mutex); } -} -EXPORT_SYMBOL(drm_syncobj_replace_fence); - -static int drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj) -{ - struct drm_syncobj_stub_fence *fence; - fence = kzalloc(sizeof(*fence), GFP_KERNEL); - if (fence == NULL) - return -ENOMEM; - spin_lock_init(&fence->lock); - dma_fence_init(&fence->base, &drm_syncobj_stub_fence_ops, - &fence->lock, 0, 0); - dma_fence_signal(&fence->base); + spin_unlock(&syncobj->lock); - drm_syncobj_replace_fence(syncobj, 0, &fence->base); - - dma_fence_put(&fence->base); - - return 0; -} - -static int -drm_syncobj_point_get(struct drm_syncobj *syncobj, u64 point, u64 flags, - struct dma_fence **fence) -{ - int ret = 0; - - if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) { - ret = wait_event_interruptible(syncobj->wq, - point <= syncobj->signal_point); - if (ret < 0) - return ret; - } - spin_lock(&syncobj->pt_lock); - *fence = drm_syncobj_find_signal_pt_for_point(syncobj, point); - if (!*fence) - ret = -EINVAL; - spin_unlock(&syncobj->pt_lock); - return ret; + dma_fence_put(old_fence); } +EXPORT_SYMBOL(drm_syncobj_replace_fence); /** - * drm_syncobj_search_fence - lookup and reference the fence in a sync object or - * in a timeline point - * @syncobj: sync object pointer - * @point: timeline point - * @flags: DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT or not - * @fence: out parameter for the fence - * - * if flags is DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT, the function will block - * here until specific timeline points is reached. - * if not, you need a submit thread and block in userspace until all future - * timeline points have materialized, only then you can submit to the kernel, - * otherwise, function will fail to return fence. + * drm_syncobj_assign_null_handle - assign a stub fence to the sync object + * @syncobj: sync object to assign the fence on * - * Returns 0 on success or a negative error value on failure. On success @fence - * contains a reference to the fence, which must be released by calling - * dma_fence_put(). + * Assign a already signaled stub fence to the sync object. */ -int drm_syncobj_search_fence(struct drm_syncobj *syncobj, u64 point, - u64 flags, struct dma_fence **fence) +static void drm_syncobj_assign_null_handle(struct drm_syncobj *syncobj) { - u64 pt_value = point; - - if (!syncobj) - return -ENOENT; + struct dma_fence *fence = dma_fence_get_stub(); - drm_syncobj_garbage_collection(syncobj); - if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) { - /*BINARY syncobj always wait on last pt */ - pt_value = syncobj->signal_point; - - if (pt_value == 0) - pt_value += DRM_SYNCOBJ_BINARY_POINT; - } - return drm_syncobj_point_get(syncobj, pt_value, flags, fence); + drm_syncobj_replace_fence(syncobj, fence); + dma_fence_put(fence); } -EXPORT_SYMBOL(drm_syncobj_search_fence); /** * drm_syncobj_find_fence - lookup and reference the fence in a sync object @@ -429,7 +180,7 @@ EXPORT_SYMBOL(drm_syncobj_search_fence); * @fence: out parameter for the fence * * This is just a convenience function that combines drm_syncobj_find() and - * drm_syncobj_lookup_fence(). + * drm_syncobj_fence_get(). * * Returns 0 on success or a negative error value on failure. On success @fence * contains a reference to the fence, which must be released by calling @@ -440,11 +191,16 @@ int drm_syncobj_find_fence(struct drm_file *file_private, struct dma_fence **fence) { struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle); - int ret; + int ret = 0; - ret = drm_syncobj_search_fence(syncobj, point, flags, fence); - if (syncobj) - drm_syncobj_put(syncobj); + if (!syncobj) + return -ENOENT; + + *fence = drm_syncobj_fence_get(syncobj); + if (!*fence) { + ret = -EINVAL; + } + drm_syncobj_put(syncobj); return ret; } EXPORT_SYMBOL(drm_syncobj_find_fence); @@ -460,7 +216,7 @@ void drm_syncobj_free(struct kref *kref) struct drm_syncobj *syncobj = container_of(kref, struct drm_syncobj, refcount); - drm_syncobj_fini(syncobj); + drm_syncobj_replace_fence(syncobj, NULL); kfree(syncobj); } EXPORT_SYMBOL(drm_syncobj_free); @@ -480,7 +236,6 @@ EXPORT_SYMBOL(drm_syncobj_free); int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags, struct dma_fence *fence) { - int ret; struct drm_syncobj *syncobj; syncobj = kzalloc(sizeof(struct drm_syncobj), GFP_KERNEL); @@ -489,24 +244,13 @@ int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags, kref_init(&syncobj->refcount); INIT_LIST_HEAD(&syncobj->cb_list); - spin_lock_init(&syncobj->pt_lock); - mutex_init(&syncobj->cb_mutex); - if (flags & DRM_SYNCOBJ_CREATE_TYPE_TIMELINE) - syncobj->type = DRM_SYNCOBJ_TYPE_TIMELINE; - else - syncobj->type = DRM_SYNCOBJ_TYPE_BINARY; - drm_syncobj_init(syncobj); - - if (flags & DRM_SYNCOBJ_CREATE_SIGNALED) { - ret = drm_syncobj_assign_null_handle(syncobj); - if (ret < 0) { - drm_syncobj_put(syncobj); - return ret; - } - } + spin_lock_init(&syncobj->lock); + + if (flags & DRM_SYNCOBJ_CREATE_SIGNALED) + drm_syncobj_assign_null_handle(syncobj); if (fence) - drm_syncobj_replace_fence(syncobj, 0, fence); + drm_syncobj_replace_fence(syncobj, fence); *out_syncobj = syncobj; return 0; @@ -691,7 +435,7 @@ static int drm_syncobj_import_sync_file_fence(struct drm_file *file_private, return -ENOENT; } - drm_syncobj_replace_fence(syncobj, 0, fence); + drm_syncobj_replace_fence(syncobj, fence); dma_fence_put(fence); drm_syncobj_put(syncobj); return 0; @@ -778,8 +522,7 @@ drm_syncobj_create_ioctl(struct drm_device *dev, void *data, return -EOPNOTSUPP; /* no valid flags yet */ - if (args->flags & ~(DRM_SYNCOBJ_CREATE_SIGNALED | - DRM_SYNCOBJ_CREATE_TYPE_TIMELINE)) + if (args->flags & ~DRM_SYNCOBJ_CREATE_SIGNALED) return -EINVAL; return drm_syncobj_create_as_handle(file_private, @@ -850,13 +593,6 @@ drm_syncobj_fd_to_handle_ioctl(struct drm_device *dev, void *data, &args->handle); } -struct syncobj_wait_entry { - struct task_struct *task; - struct dma_fence *fence; - struct dma_fence_cb fence_cb; - struct drm_syncobj_cb syncobj_cb; -}; - static void syncobj_wait_fence_func(struct dma_fence *fence, struct dma_fence_cb *cb) { @@ -867,13 +603,11 @@ static void syncobj_wait_fence_func(struct dma_fence *fence, } static void syncobj_wait_syncobj_func(struct drm_syncobj *syncobj, - struct drm_syncobj_cb *cb) + struct syncobj_wait_entry *wait) { - struct syncobj_wait_entry *wait = - container_of(cb, struct syncobj_wait_entry, syncobj_cb); - - drm_syncobj_search_fence(syncobj, 0, 0, &wait->fence); - + /* This happens inside the syncobj lock */ + wait->fence = dma_fence_get(rcu_dereference_protected(syncobj->fence, + lockdep_is_held(&syncobj->lock))); wake_up_process(wait->task); } @@ -899,8 +633,7 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs, signaled_count = 0; for (i = 0; i < count; ++i) { entries[i].task = current; - drm_syncobj_search_fence(syncobjs[i], 0, 0, - &entries[i].fence); + entries[i].fence = drm_syncobj_fence_get(syncobjs[i]); if (!entries[i].fence) { if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) { continue; @@ -930,15 +663,8 @@ static signed long drm_syncobj_array_wait_timeout(struct drm_syncobj **syncobjs, */ if (flags & DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) { - for (i = 0; i < count; ++i) { - if (entries[i].fence) - continue; - - drm_syncobj_fence_get_or_add_callback(syncobjs[i], - &entries[i].fence, - &entries[i].syncobj_cb, - syncobj_wait_syncobj_func); - } + for (i = 0; i < count; ++i) + drm_syncobj_fence_add_wait(syncobjs[i], &entries[i]); } do { @@ -987,9 +713,7 @@ done_waiting: cleanup_entries: for (i = 0; i < count; ++i) { - if (entries[i].syncobj_cb.func) - drm_syncobj_remove_callback(syncobjs[i], - &entries[i].syncobj_cb); + drm_syncobj_remove_wait(syncobjs[i], &entries[i]); if (entries[i].fence_cb.func) dma_fence_remove_callback(entries[i].fence, &entries[i].fence_cb); @@ -1165,13 +889,12 @@ drm_syncobj_reset_ioctl(struct drm_device *dev, void *data, if (ret < 0) return ret; - for (i = 0; i < args->count_handles; i++) { - drm_syncobj_fini(syncobjs[i]); - drm_syncobj_init(syncobjs[i]); - } + for (i = 0; i < args->count_handles; i++) + drm_syncobj_replace_fence(syncobjs[i], NULL); + drm_syncobj_array_free(syncobjs, args->count_handles); - return ret; + return 0; } int @@ -1199,11 +922,8 @@ drm_syncobj_signal_ioctl(struct drm_device *dev, void *data, if (ret < 0) return ret; - for (i = 0; i < args->count_handles; i++) { - ret = drm_syncobj_assign_null_handle(syncobjs[i]); - if (ret < 0) - break; - } + for (i = 0; i < args->count_handles; i++) + drm_syncobj_assign_null_handle(syncobjs[i]); drm_syncobj_array_free(syncobjs, args->count_handles); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 83c1f46670bf..52802e6049e0 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -550,7 +550,7 @@ out_register: out_bind: kfree(priv); out_unref: - drm_dev_unref(drm); + drm_dev_put(drm); return ret; } @@ -567,7 +567,7 @@ static void etnaviv_unbind(struct device *dev) drm->dev_private = NULL; kfree(priv); - drm_dev_unref(drm); + drm_dev_put(drm); } static const struct component_master_ops etnaviv_master_ops = { diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 1fa74226db91..5c48915f492d 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -449,7 +449,7 @@ static void etnaviv_gem_describe_fence(struct dma_fence *fence, const char *type, struct seq_file *m) { if (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) - seq_printf(m, "\t%9s: %s %s seq %u\n", + seq_printf(m, "\t%9s: %s %s seq %llu\n", type, fence->ops->get_driver_name(fence), fence->ops->get_timeline_name(fence), diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c55b1f75c980..b70de1a9e899 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3187,7 +3187,7 @@ i915_gem_reset_request(struct intel_engine_cs *engine, */ if (i915_request_completed(request)) { - GEM_TRACE("%s pardoned global=%d (fence %llx:%d), current %d\n", + GEM_TRACE("%s pardoned global=%d (fence %llx:%lld), current %d\n", engine->name, request->global_seqno, request->fence.context, request->fence.seqno, intel_engine_get_seqno(engine)); @@ -3321,7 +3321,7 @@ static void nop_complete_submit_request(struct i915_request *request) { unsigned long flags; - GEM_TRACE("%s fence %llx:%d -> -EIO\n", + GEM_TRACE("%s fence %llx:%lld -> -EIO\n", request->engine->name, request->fence.context, request->fence.seqno); dma_fence_set_error(&request->fence, -EIO); diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index b97963db0287..66117a8281ef 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -653,7 +653,7 @@ last_request_on_engine(struct i915_timeline *timeline, rq = i915_gem_active_raw(&timeline->last_request, &engine->i915->drm.struct_mutex); if (rq && rq->engine == engine) { - GEM_TRACE("last request for %s on engine %s: %llx:%d\n", + GEM_TRACE("last request for %s on engine %s: %llx:%llu\n", timeline->name, engine->name, rq->fence.context, rq->fence.seqno); GEM_BUG_ON(rq->timeline != timeline); @@ -690,14 +690,14 @@ static bool engine_has_kernel_context_barrier(struct intel_engine_cs *engine) * switch-to-kernel-context? */ if (!i915_timeline_sync_is_later(barrier, &rq->fence)) { - GEM_TRACE("%s needs barrier for %llx:%d\n", + GEM_TRACE("%s needs barrier for %llx:%lld\n", ring->timeline->name, rq->fence.context, rq->fence.seqno); return false; } - GEM_TRACE("%s has barrier after %llx:%d\n", + GEM_TRACE("%s has barrier after %llx:%lld\n", ring->timeline->name, rq->fence.context, rq->fence.seqno); @@ -753,7 +753,7 @@ int i915_gem_switch_to_kernel_context(struct drm_i915_private *i915) if (prev->gem_context == i915->kernel_context) continue; - GEM_TRACE("add barrier on %s for %llx:%d\n", + GEM_TRACE("add barrier on %s for %llx:%lld\n", engine->name, prev->fence.context, prev->fence.seqno); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 7b3ae2333dbf..10a4afb4f235 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -2162,7 +2162,7 @@ await_fence_array(struct i915_execbuffer *eb, if (!(flags & I915_EXEC_FENCE_WAIT)) continue; - drm_syncobj_search_fence(syncobj, 0, 0, &fence); + fence = drm_syncobj_fence_get(syncobj); if (!fence) return -EINVAL; @@ -2191,7 +2191,7 @@ signal_fence_array(struct i915_execbuffer *eb, if (!(flags & I915_EXEC_FENCE_SIGNAL)) continue; - drm_syncobj_replace_fence(syncobj, 0, fence); + drm_syncobj_replace_fence(syncobj, fence); } } diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index 71107540581d..191703986c7b 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -267,7 +267,7 @@ static void free_capture_list(struct i915_request *request) static void __retire_engine_request(struct intel_engine_cs *engine, struct i915_request *rq) { - GEM_TRACE("%s(%s) fence %llx:%d, global=%d, current %d\n", + GEM_TRACE("%s(%s) fence %llx:%lld, global=%d, current %d\n", __func__, engine->name, rq->fence.context, rq->fence.seqno, rq->global_seqno, @@ -329,7 +329,7 @@ static void i915_request_retire(struct i915_request *request) { struct i915_gem_active *active, *next; - GEM_TRACE("%s fence %llx:%d, global=%d, current %d\n", + GEM_TRACE("%s fence %llx:%lld, global=%d, current %d\n", request->engine->name, request->fence.context, request->fence.seqno, request->global_seqno, @@ -392,7 +392,7 @@ void i915_request_retire_upto(struct i915_request *rq) struct intel_ring *ring = rq->ring; struct i915_request *tmp; - GEM_TRACE("%s fence %llx:%d, global=%d, current %d\n", + GEM_TRACE("%s fence %llx:%lld, global=%d, current %d\n", rq->engine->name, rq->fence.context, rq->fence.seqno, rq->global_seqno, @@ -433,7 +433,7 @@ void __i915_request_submit(struct i915_request *request) struct intel_engine_cs *engine = request->engine; u32 seqno; - GEM_TRACE("%s fence %llx:%d -> global=%d, current %d\n", + GEM_TRACE("%s fence %llx:%lld -> global=%d, current %d\n", engine->name, request->fence.context, request->fence.seqno, engine->timeline.seqno + 1, @@ -483,7 +483,7 @@ void __i915_request_unsubmit(struct i915_request *request) { struct intel_engine_cs *engine = request->engine; - GEM_TRACE("%s fence %llx:%d <- global=%d, current %d\n", + GEM_TRACE("%s fence %llx:%lld <- global=%d, current %d\n", engine->name, request->fence.context, request->fence.seqno, request->global_seqno, @@ -958,7 +958,7 @@ void i915_request_add(struct i915_request *request) struct i915_request *prev; u32 *cs; - GEM_TRACE("%s fence %llx:%d\n", + GEM_TRACE("%s fence %llx:%lld\n", engine->name, request->fence.context, request->fence.seqno); lockdep_assert_held(&request->i915->drm.struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_sw_fence.c b/drivers/gpu/drm/i915/i915_sw_fence.c index 6dbeed079ae5..11bcdabd5177 100644 --- a/drivers/gpu/drm/i915/i915_sw_fence.c +++ b/drivers/gpu/drm/i915/i915_sw_fence.c @@ -393,7 +393,7 @@ static void timer_i915_sw_fence_wake(struct timer_list *t) if (!fence) return; - pr_notice("Asynchronous wait on fence %s:%s:%x timed out (hint:%pS)\n", + pr_notice("Asynchronous wait on fence %s:%s:%llx timed out (hint:%pS)\n", cb->dma->ops->get_driver_name(cb->dma), cb->dma->ops->get_timeline_name(cb->dma), cb->dma->seqno, diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c index 4de247ddf05f..f05427b74e34 100644 --- a/drivers/gpu/drm/i915/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/intel_dp_mst.c @@ -517,20 +517,10 @@ static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr, drm_connector_put(connector); } -static void intel_dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr) -{ - struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr); - struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); - struct drm_device *dev = intel_dig_port->base.base.dev; - - drm_kms_helper_hotplug_event(dev); -} - static const struct drm_dp_mst_topology_cbs mst_cbs = { .add_connector = intel_dp_add_mst_connector, .register_connector = intel_dp_register_mst_connector, .destroy_connector = intel_dp_destroy_mst_connector, - .hotplug = intel_dp_mst_hotplug, }; static struct intel_dp_mst_encoder * diff --git a/drivers/gpu/drm/i915/intel_engine_cs.c b/drivers/gpu/drm/i915/intel_engine_cs.c index 759c0fd58f8c..dfafa79171df 100644 --- a/drivers/gpu/drm/i915/intel_engine_cs.c +++ b/drivers/gpu/drm/i915/intel_engine_cs.c @@ -1239,7 +1239,7 @@ static void print_request(struct drm_printer *m, x = print_sched_attr(rq->i915, &rq->sched.attr, buf, x, sizeof(buf)); - drm_printf(m, "%s%x%s [%llx:%x]%s @ %dms: %s\n", + drm_printf(m, "%s%x%s [%llx:%llx]%s @ %dms: %s\n", prefix, rq->global_seqno, i915_request_completed(rq) ? "!" : "", diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 08fd9b12e4d7..9399db3260ad 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -450,7 +450,7 @@ static void execlists_submit_ports(struct intel_engine_cs *engine) desc = execlists_update_context(rq); GEM_DEBUG_EXEC(port[n].context_id = upper_32_bits(desc)); - GEM_TRACE("%s in[%d]: ctx=%d.%d, global=%d (fence %llx:%d) (current %d), prio=%d\n", + GEM_TRACE("%s in[%d]: ctx=%d.%d, global=%d (fence %llx:%lld) (current %d), prio=%d\n", engine->name, n, port[n].context_id, count, rq->global_seqno, @@ -743,7 +743,7 @@ execlists_cancel_port_requests(struct intel_engine_execlists * const execlists) while (num_ports-- && port_isset(port)) { struct i915_request *rq = port_request(port); - GEM_TRACE("%s:port%u global=%d (fence %llx:%d), (current %d)\n", + GEM_TRACE("%s:port%u global=%d (fence %llx:%lld), (current %d)\n", rq->engine->name, (unsigned int)(port - execlists->port), rq->global_seqno, @@ -952,7 +952,7 @@ static void process_csb(struct intel_engine_cs *engine) EXECLISTS_ACTIVE_USER)); rq = port_unpack(port, &count); - GEM_TRACE("%s out[0]: ctx=%d.%d, global=%d (fence %llx:%d) (current %d), prio=%d\n", + GEM_TRACE("%s out[0]: ctx=%d.%d, global=%d (fence %llx:%lld) (current %d), prio=%d\n", engine->name, port->context_id, count, rq ? rq->global_seqno : 0, diff --git a/drivers/gpu/drm/i915/selftests/intel_hangcheck.c b/drivers/gpu/drm/i915/selftests/intel_hangcheck.c index defe671130ab..33494d922fab 100644 --- a/drivers/gpu/drm/i915/selftests/intel_hangcheck.c +++ b/drivers/gpu/drm/i915/selftests/intel_hangcheck.c @@ -478,7 +478,7 @@ static int __igt_reset_engine(struct drm_i915_private *i915, bool active) if (!wait_until_running(&h, rq)) { struct drm_printer p = drm_info_printer(i915->drm.dev); - pr_err("%s: Failed to start request %x, at %x\n", + pr_err("%s: Failed to start request %llx, at %x\n", __func__, rq->fence.seqno, hws_seqno(&h, rq)); intel_engine_dump(engine, &p, "%s\n", engine->name); @@ -579,7 +579,7 @@ static int active_request_put(struct i915_request *rq) return 0; if (i915_request_wait(rq, 0, 5 * HZ) < 0) { - GEM_TRACE("%s timed out waiting for completion of fence %llx:%d, seqno %d.\n", + GEM_TRACE("%s timed out waiting for completion of fence %llx:%lld, seqno %d.\n", rq->engine->name, rq->fence.context, rq->fence.seqno, @@ -756,7 +756,7 @@ static int __igt_reset_engines(struct drm_i915_private *i915, if (!wait_until_running(&h, rq)) { struct drm_printer p = drm_info_printer(i915->drm.dev); - pr_err("%s: Failed to start request %x, at %x\n", + pr_err("%s: Failed to start request %llx, at %x\n", __func__, rq->fence.seqno, hws_seqno(&h, rq)); intel_engine_dump(engine, &p, "%s\n", engine->name); @@ -955,7 +955,7 @@ static int igt_reset_wait(void *arg) if (!wait_until_running(&h, rq)) { struct drm_printer p = drm_info_printer(i915->drm.dev); - pr_err("%s: Failed to start request %x, at %x\n", + pr_err("%s: Failed to start request %llx, at %x\n", __func__, rq->fence.seqno, hws_seqno(&h, rq)); intel_engine_dump(rq->engine, &p, "%s\n", rq->engine->name); @@ -1134,7 +1134,7 @@ static int __igt_reset_evict_vma(struct drm_i915_private *i915, if (!wait_until_running(&h, rq)) { struct drm_printer p = drm_info_printer(i915->drm.dev); - pr_err("%s: Failed to start request %x, at %x\n", + pr_err("%s: Failed to start request %llx, at %x\n", __func__, rq->fence.seqno, hws_seqno(&h, rq)); intel_engine_dump(rq->engine, &p, "%s\n", rq->engine->name); @@ -1329,7 +1329,7 @@ static int igt_reset_queue(void *arg) if (!wait_until_running(&h, prev)) { struct drm_printer p = drm_info_printer(i915->drm.dev); - pr_err("%s(%s): Failed to start request %x, at %x\n", + pr_err("%s(%s): Failed to start request %llx, at %x\n", __func__, engine->name, prev->fence.seqno, hws_seqno(&h, prev)); intel_engine_dump(engine, &p, @@ -1440,7 +1440,7 @@ static int igt_handle_error(void *arg) if (!wait_until_running(&h, rq)) { struct drm_printer p = drm_info_printer(i915->drm.dev); - pr_err("%s: Failed to start request %x, at %x\n", + pr_err("%s: Failed to start request %llx, at %x\n", __func__, rq->fence.seqno, hws_seqno(&h, rq)); intel_engine_dump(rq->engine, &p, "%s\n", rq->engine->name); diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig index 3ce51d8dfe1c..c28b69f48555 100644 --- a/drivers/gpu/drm/meson/Kconfig +++ b/drivers/gpu/drm/meson/Kconfig @@ -7,6 +7,7 @@ config DRM_MESON select DRM_GEM_CMA_HELPER select VIDEOMODE_HELPERS select REGMAP_MMIO + select MESON_CANVAS config DRM_MESON_DW_HDMI tristate "HDMI Synopsys Controller support for Amlogic Meson Display" diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile index c5c4cc362f02..7709f2fbb9f7 100644 --- a/drivers/gpu/drm/meson/Makefile +++ b/drivers/gpu/drm/meson/Makefile @@ -1,5 +1,5 @@ meson-drm-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o -meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o +meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o meson_overlay.o obj-$(CONFIG_DRM_MESON) += meson-drm.o obj-$(CONFIG_DRM_MESON_DW_HDMI) += meson_dw_hdmi.o diff --git a/drivers/gpu/drm/meson/meson_canvas.c b/drivers/gpu/drm/meson/meson_canvas.c index 08f6073d967e..5de11aa7c775 100644 --- a/drivers/gpu/drm/meson/meson_canvas.c +++ b/drivers/gpu/drm/meson/meson_canvas.c @@ -39,6 +39,7 @@ #define CANVAS_WIDTH_HBIT 0 #define CANVAS_HEIGHT_BIT 9 #define CANVAS_BLKMODE_BIT 24 +#define CANVAS_ENDIAN_BIT 26 #define DMC_CAV_LUT_ADDR 0x50 /* 0x14 offset in data sheet */ #define CANVAS_LUT_WR_EN (0x2 << 8) #define CANVAS_LUT_RD_EN (0x1 << 8) @@ -47,7 +48,8 @@ void meson_canvas_setup(struct meson_drm *priv, uint32_t canvas_index, uint32_t addr, uint32_t stride, uint32_t height, unsigned int wrap, - unsigned int blkmode) + unsigned int blkmode, + unsigned int endian) { unsigned int val; @@ -60,7 +62,8 @@ void meson_canvas_setup(struct meson_drm *priv, CANVAS_WIDTH_HBIT) | (height << CANVAS_HEIGHT_BIT) | (wrap << 22) | - (blkmode << CANVAS_BLKMODE_BIT)); + (blkmode << CANVAS_BLKMODE_BIT) | + (endian << CANVAS_ENDIAN_BIT)); regmap_write(priv->dmc, DMC_CAV_LUT_ADDR, CANVAS_LUT_WR_EN | canvas_index); diff --git a/drivers/gpu/drm/meson/meson_canvas.h b/drivers/gpu/drm/meson/meson_canvas.h index af1759da4b27..85dbf26e2826 100644 --- a/drivers/gpu/drm/meson/meson_canvas.h +++ b/drivers/gpu/drm/meson/meson_canvas.h @@ -23,6 +23,9 @@ #define __MESON_CANVAS_H #define MESON_CANVAS_ID_OSD1 0x4e +#define MESON_CANVAS_ID_VD1_0 0x60 +#define MESON_CANVAS_ID_VD1_1 0x61 +#define MESON_CANVAS_ID_VD1_2 0x62 /* Canvas configuration. */ #define MESON_CANVAS_WRAP_NONE 0x00 @@ -33,10 +36,16 @@ #define MESON_CANVAS_BLKMODE_32x32 0x01 #define MESON_CANVAS_BLKMODE_64x64 0x02 +#define MESON_CANVAS_ENDIAN_SWAP16 0x1 +#define MESON_CANVAS_ENDIAN_SWAP32 0x3 +#define MESON_CANVAS_ENDIAN_SWAP64 0x7 +#define MESON_CANVAS_ENDIAN_SWAP128 0xf + void meson_canvas_setup(struct meson_drm *priv, uint32_t canvas_index, uint32_t addr, uint32_t stride, uint32_t height, unsigned int wrap, - unsigned int blkmode); + unsigned int blkmode, + unsigned int endian); #endif /* __MESON_CANVAS_H */ diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c index 05520202c967..d78168f979db 100644 --- a/drivers/gpu/drm/meson/meson_crtc.c +++ b/drivers/gpu/drm/meson/meson_crtc.c @@ -25,6 +25,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/platform_device.h> +#include <linux/bitfield.h> #include <drm/drmP.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> @@ -98,6 +99,10 @@ static void meson_crtc_atomic_enable(struct drm_crtc *crtc, writel(crtc_state->mode.hdisplay, priv->io_base + _REG(VPP_POSTBLEND_H_SIZE)); + /* VD1 Preblend vertical start/end */ + writel(FIELD_PREP(GENMASK(11, 0), 2303), + priv->io_base + _REG(VPP_PREBLEND_VD1_V_START_END)); + writel_bits_relaxed(VPP_POSTBLEND_ENABLE, VPP_POSTBLEND_ENABLE, priv->io_base + _REG(VPP_MISC)); @@ -110,11 +115,17 @@ static void meson_crtc_atomic_disable(struct drm_crtc *crtc, struct meson_crtc *meson_crtc = to_meson_crtc(crtc); struct meson_drm *priv = meson_crtc->priv; + DRM_DEBUG_DRIVER("\n"); + priv->viu.osd1_enabled = false; priv->viu.osd1_commit = false; + priv->viu.vd1_enabled = false; + priv->viu.vd1_commit = false; + /* Disable VPP Postblend */ - writel_bits_relaxed(VPP_POSTBLEND_ENABLE, 0, + writel_bits_relaxed(VPP_OSD1_POSTBLEND | VPP_VD1_POSTBLEND | + VPP_VD1_PREBLEND | VPP_POSTBLEND_ENABLE, 0, priv->io_base + _REG(VPP_MISC)); if (crtc->state->event && !crtc->state->active) { @@ -149,6 +160,7 @@ static void meson_crtc_atomic_flush(struct drm_crtc *crtc, struct meson_drm *priv = meson_crtc->priv; priv->viu.osd1_commit = true; + priv->viu.vd1_commit = true; } static const struct drm_crtc_helper_funcs meson_crtc_helper_funcs = { @@ -177,26 +189,37 @@ void meson_crtc_irq(struct meson_drm *priv) priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W3)); writel_relaxed(priv->viu.osd1_blk0_cfg[4], priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W4)); - - /* If output is interlace, make use of the Scaler */ - if (priv->viu.osd1_interlace) { - struct drm_plane *plane = priv->primary_plane; - struct drm_plane_state *state = plane->state; - struct drm_rect dest = { - .x1 = state->crtc_x, - .y1 = state->crtc_y, - .x2 = state->crtc_x + state->crtc_w, - .y2 = state->crtc_y + state->crtc_h, - }; - - meson_vpp_setup_interlace_vscaler_osd1(priv, &dest); - } else - meson_vpp_disable_interlace_vscaler_osd1(priv); - - meson_canvas_setup(priv, MESON_CANVAS_ID_OSD1, - priv->viu.osd1_addr, priv->viu.osd1_stride, - priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE, - MESON_CANVAS_BLKMODE_LINEAR); + writel_relaxed(priv->viu.osd_sc_ctrl0, + priv->io_base + _REG(VPP_OSD_SC_CTRL0)); + writel_relaxed(priv->viu.osd_sc_i_wh_m1, + priv->io_base + _REG(VPP_OSD_SCI_WH_M1)); + writel_relaxed(priv->viu.osd_sc_o_h_start_end, + priv->io_base + _REG(VPP_OSD_SCO_H_START_END)); + writel_relaxed(priv->viu.osd_sc_o_v_start_end, + priv->io_base + _REG(VPP_OSD_SCO_V_START_END)); + writel_relaxed(priv->viu.osd_sc_v_ini_phase, + priv->io_base + _REG(VPP_OSD_VSC_INI_PHASE)); + writel_relaxed(priv->viu.osd_sc_v_phase_step, + priv->io_base + _REG(VPP_OSD_VSC_PHASE_STEP)); + writel_relaxed(priv->viu.osd_sc_h_ini_phase, + priv->io_base + _REG(VPP_OSD_HSC_INI_PHASE)); + writel_relaxed(priv->viu.osd_sc_h_phase_step, + priv->io_base + _REG(VPP_OSD_HSC_PHASE_STEP)); + writel_relaxed(priv->viu.osd_sc_h_ctrl0, + priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); + writel_relaxed(priv->viu.osd_sc_v_ctrl0, + priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); + + if (priv->canvas) + meson_canvas_config(priv->canvas, priv->canvas_id_osd1, + priv->viu.osd1_addr, priv->viu.osd1_stride, + priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, 0); + else + meson_canvas_setup(priv, MESON_CANVAS_ID_OSD1, + priv->viu.osd1_addr, priv->viu.osd1_stride, + priv->viu.osd1_height, MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, 0); /* Enable OSD1 */ writel_bits_relaxed(VPP_OSD1_POSTBLEND, VPP_OSD1_POSTBLEND, @@ -205,6 +228,206 @@ void meson_crtc_irq(struct meson_drm *priv) priv->viu.osd1_commit = false; } + /* Update the VD1 registers */ + if (priv->viu.vd1_enabled && priv->viu.vd1_commit) { + + switch (priv->viu.vd1_planes) { + case 3: + if (priv->canvas) + meson_canvas_config(priv->canvas, + priv->canvas_id_vd1_2, + priv->viu.vd1_addr2, + priv->viu.vd1_stride2, + priv->viu.vd1_height2, + MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + else + meson_canvas_setup(priv, MESON_CANVAS_ID_VD1_2, + priv->viu.vd1_addr2, + priv->viu.vd1_stride2, + priv->viu.vd1_height2, + MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + /* fallthrough */ + case 2: + if (priv->canvas) + meson_canvas_config(priv->canvas, + priv->canvas_id_vd1_1, + priv->viu.vd1_addr1, + priv->viu.vd1_stride1, + priv->viu.vd1_height1, + MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + else + meson_canvas_setup(priv, MESON_CANVAS_ID_VD1_1, + priv->viu.vd1_addr2, + priv->viu.vd1_stride2, + priv->viu.vd1_height2, + MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + /* fallthrough */ + case 1: + if (priv->canvas) + meson_canvas_config(priv->canvas, + priv->canvas_id_vd1_0, + priv->viu.vd1_addr0, + priv->viu.vd1_stride0, + priv->viu.vd1_height0, + MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + else + meson_canvas_setup(priv, MESON_CANVAS_ID_VD1_0, + priv->viu.vd1_addr2, + priv->viu.vd1_stride2, + priv->viu.vd1_height2, + MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR, + MESON_CANVAS_ENDIAN_SWAP64); + }; + + writel_relaxed(priv->viu.vd1_if0_gen_reg, + priv->io_base + _REG(VD1_IF0_GEN_REG)); + writel_relaxed(priv->viu.vd1_if0_gen_reg, + priv->io_base + _REG(VD2_IF0_GEN_REG)); + writel_relaxed(priv->viu.vd1_if0_gen_reg2, + priv->io_base + _REG(VD1_IF0_GEN_REG2)); + writel_relaxed(priv->viu.viu_vd1_fmt_ctrl, + priv->io_base + _REG(VIU_VD1_FMT_CTRL)); + writel_relaxed(priv->viu.viu_vd1_fmt_ctrl, + priv->io_base + _REG(VIU_VD2_FMT_CTRL)); + writel_relaxed(priv->viu.viu_vd1_fmt_w, + priv->io_base + _REG(VIU_VD1_FMT_W)); + writel_relaxed(priv->viu.viu_vd1_fmt_w, + priv->io_base + _REG(VIU_VD2_FMT_W)); + writel_relaxed(priv->viu.vd1_if0_canvas0, + priv->io_base + _REG(VD1_IF0_CANVAS0)); + writel_relaxed(priv->viu.vd1_if0_canvas0, + priv->io_base + _REG(VD1_IF0_CANVAS1)); + writel_relaxed(priv->viu.vd1_if0_canvas0, + priv->io_base + _REG(VD2_IF0_CANVAS0)); + writel_relaxed(priv->viu.vd1_if0_canvas0, + priv->io_base + _REG(VD2_IF0_CANVAS1)); + writel_relaxed(priv->viu.vd1_if0_luma_x0, + priv->io_base + _REG(VD1_IF0_LUMA_X0)); + writel_relaxed(priv->viu.vd1_if0_luma_x0, + priv->io_base + _REG(VD1_IF0_LUMA_X1)); + writel_relaxed(priv->viu.vd1_if0_luma_x0, + priv->io_base + _REG(VD2_IF0_LUMA_X0)); + writel_relaxed(priv->viu.vd1_if0_luma_x0, + priv->io_base + _REG(VD2_IF0_LUMA_X1)); + writel_relaxed(priv->viu.vd1_if0_luma_y0, + priv->io_base + _REG(VD1_IF0_LUMA_Y0)); + writel_relaxed(priv->viu.vd1_if0_luma_y0, + priv->io_base + _REG(VD1_IF0_LUMA_Y1)); + writel_relaxed(priv->viu.vd1_if0_luma_y0, + priv->io_base + _REG(VD2_IF0_LUMA_Y0)); + writel_relaxed(priv->viu.vd1_if0_luma_y0, + priv->io_base + _REG(VD2_IF0_LUMA_Y1)); + writel_relaxed(priv->viu.vd1_if0_chroma_x0, + priv->io_base + _REG(VD1_IF0_CHROMA_X0)); + writel_relaxed(priv->viu.vd1_if0_chroma_x0, + priv->io_base + _REG(VD1_IF0_CHROMA_X1)); + writel_relaxed(priv->viu.vd1_if0_chroma_x0, + priv->io_base + _REG(VD2_IF0_CHROMA_X0)); + writel_relaxed(priv->viu.vd1_if0_chroma_x0, + priv->io_base + _REG(VD2_IF0_CHROMA_X1)); + writel_relaxed(priv->viu.vd1_if0_chroma_y0, + priv->io_base + _REG(VD1_IF0_CHROMA_Y0)); + writel_relaxed(priv->viu.vd1_if0_chroma_y0, + priv->io_base + _REG(VD1_IF0_CHROMA_Y1)); + writel_relaxed(priv->viu.vd1_if0_chroma_y0, + priv->io_base + _REG(VD2_IF0_CHROMA_Y0)); + writel_relaxed(priv->viu.vd1_if0_chroma_y0, + priv->io_base + _REG(VD2_IF0_CHROMA_Y1)); + writel_relaxed(priv->viu.vd1_if0_repeat_loop, + priv->io_base + _REG(VD1_IF0_RPT_LOOP)); + writel_relaxed(priv->viu.vd1_if0_repeat_loop, + priv->io_base + _REG(VD2_IF0_RPT_LOOP)); + writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat, + priv->io_base + _REG(VD1_IF0_LUMA0_RPT_PAT)); + writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat, + priv->io_base + _REG(VD2_IF0_LUMA0_RPT_PAT)); + writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat, + priv->io_base + _REG(VD1_IF0_LUMA1_RPT_PAT)); + writel_relaxed(priv->viu.vd1_if0_luma0_rpt_pat, + priv->io_base + _REG(VD2_IF0_LUMA1_RPT_PAT)); + writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat, + priv->io_base + _REG(VD1_IF0_CHROMA0_RPT_PAT)); + writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat, + priv->io_base + _REG(VD2_IF0_CHROMA0_RPT_PAT)); + writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat, + priv->io_base + _REG(VD1_IF0_CHROMA1_RPT_PAT)); + writel_relaxed(priv->viu.vd1_if0_chroma0_rpt_pat, + priv->io_base + _REG(VD2_IF0_CHROMA1_RPT_PAT)); + writel_relaxed(0, priv->io_base + _REG(VD1_IF0_LUMA_PSEL)); + writel_relaxed(0, priv->io_base + _REG(VD1_IF0_CHROMA_PSEL)); + writel_relaxed(0, priv->io_base + _REG(VD2_IF0_LUMA_PSEL)); + writel_relaxed(0, priv->io_base + _REG(VD2_IF0_CHROMA_PSEL)); + writel_relaxed(priv->viu.vd1_range_map_y, + priv->io_base + _REG(VD1_IF0_RANGE_MAP_Y)); + writel_relaxed(priv->viu.vd1_range_map_cb, + priv->io_base + _REG(VD1_IF0_RANGE_MAP_CB)); + writel_relaxed(priv->viu.vd1_range_map_cr, + priv->io_base + _REG(VD1_IF0_RANGE_MAP_CR)); + writel_relaxed(0x78404, + priv->io_base + _REG(VPP_SC_MISC)); + writel_relaxed(priv->viu.vpp_pic_in_height, + priv->io_base + _REG(VPP_PIC_IN_HEIGHT)); + writel_relaxed(priv->viu.vpp_postblend_vd1_h_start_end, + priv->io_base + _REG(VPP_POSTBLEND_VD1_H_START_END)); + writel_relaxed(priv->viu.vpp_blend_vd2_h_start_end, + priv->io_base + _REG(VPP_BLEND_VD2_H_START_END)); + writel_relaxed(priv->viu.vpp_postblend_vd1_v_start_end, + priv->io_base + _REG(VPP_POSTBLEND_VD1_V_START_END)); + writel_relaxed(priv->viu.vpp_blend_vd2_v_start_end, + priv->io_base + _REG(VPP_BLEND_VD2_V_START_END)); + writel_relaxed(priv->viu.vpp_hsc_region12_startp, + priv->io_base + _REG(VPP_HSC_REGION12_STARTP)); + writel_relaxed(priv->viu.vpp_hsc_region34_startp, + priv->io_base + _REG(VPP_HSC_REGION34_STARTP)); + writel_relaxed(priv->viu.vpp_hsc_region4_endp, + priv->io_base + _REG(VPP_HSC_REGION4_ENDP)); + writel_relaxed(priv->viu.vpp_hsc_start_phase_step, + priv->io_base + _REG(VPP_HSC_START_PHASE_STEP)); + writel_relaxed(priv->viu.vpp_hsc_region1_phase_slope, + priv->io_base + _REG(VPP_HSC_REGION1_PHASE_SLOPE)); + writel_relaxed(priv->viu.vpp_hsc_region3_phase_slope, + priv->io_base + _REG(VPP_HSC_REGION3_PHASE_SLOPE)); + writel_relaxed(priv->viu.vpp_line_in_length, + priv->io_base + _REG(VPP_LINE_IN_LENGTH)); + writel_relaxed(priv->viu.vpp_preblend_h_size, + priv->io_base + _REG(VPP_PREBLEND_H_SIZE)); + writel_relaxed(priv->viu.vpp_vsc_region12_startp, + priv->io_base + _REG(VPP_VSC_REGION12_STARTP)); + writel_relaxed(priv->viu.vpp_vsc_region34_startp, + priv->io_base + _REG(VPP_VSC_REGION34_STARTP)); + writel_relaxed(priv->viu.vpp_vsc_region4_endp, + priv->io_base + _REG(VPP_VSC_REGION4_ENDP)); + writel_relaxed(priv->viu.vpp_vsc_start_phase_step, + priv->io_base + _REG(VPP_VSC_START_PHASE_STEP)); + writel_relaxed(priv->viu.vpp_vsc_ini_phase, + priv->io_base + _REG(VPP_VSC_INI_PHASE)); + writel_relaxed(priv->viu.vpp_vsc_phase_ctrl, + priv->io_base + _REG(VPP_VSC_PHASE_CTRL)); + writel_relaxed(priv->viu.vpp_hsc_phase_ctrl, + priv->io_base + _REG(VPP_HSC_PHASE_CTRL)); + writel_relaxed(0x42, priv->io_base + _REG(VPP_SCALE_COEF_IDX)); + + /* Enable VD1 */ + writel_bits_relaxed(VPP_VD1_PREBLEND | VPP_VD1_POSTBLEND | + VPP_COLOR_MNG_ENABLE, + VPP_VD1_PREBLEND | VPP_VD1_POSTBLEND | + VPP_COLOR_MNG_ENABLE, + priv->io_base + _REG(VPP_MISC)); + + priv->viu.vd1_commit = false; + } + drm_crtc_handle_vblank(priv->crtc); spin_lock_irqsave(&priv->drm->event_lock, flags); diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index 348b5a198b9d..3ee4d4a4ecba 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -41,6 +41,7 @@ #include "meson_drv.h" #include "meson_plane.h" +#include "meson_overlay.h" #include "meson_crtc.h" #include "meson_venc_cvbs.h" @@ -208,24 +209,51 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) goto free_drm; } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc"); - if (!res) { - ret = -EINVAL; - goto free_drm; - } - /* Simply ioremap since it may be a shared register zone */ - regs = devm_ioremap(dev, res->start, resource_size(res)); - if (!regs) { - ret = -EADDRNOTAVAIL; - goto free_drm; - } + priv->canvas = meson_canvas_get(dev); + if (!IS_ERR(priv->canvas)) { + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_osd1); + if (ret) + goto free_drm; + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_0); + if (ret) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); + goto free_drm; + } + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_1); + if (ret) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); + goto free_drm; + } + ret = meson_canvas_alloc(priv->canvas, &priv->canvas_id_vd1_2); + if (ret) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); + goto free_drm; + } + } else { + priv->canvas = NULL; - priv->dmc = devm_regmap_init_mmio(dev, regs, - &meson_regmap_config); - if (IS_ERR(priv->dmc)) { - dev_err(&pdev->dev, "Couldn't create the DMC regmap\n"); - ret = PTR_ERR(priv->dmc); - goto free_drm; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc"); + if (!res) { + ret = -EINVAL; + goto free_drm; + } + /* Simply ioremap since it may be a shared register zone */ + regs = devm_ioremap(dev, res->start, resource_size(res)); + if (!regs) { + ret = -EADDRNOTAVAIL; + goto free_drm; + } + + priv->dmc = devm_regmap_init_mmio(dev, regs, + &meson_regmap_config); + if (IS_ERR(priv->dmc)) { + dev_err(&pdev->dev, "Couldn't create the DMC regmap\n"); + ret = PTR_ERR(priv->dmc); + goto free_drm; + } } priv->vsync_irq = platform_get_irq(pdev, 0); @@ -264,6 +292,10 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) if (ret) goto free_drm; + ret = meson_overlay_create(priv); + if (ret) + goto free_drm; + ret = meson_crtc_create(priv); if (ret) goto free_drm; @@ -300,6 +332,14 @@ static int meson_drv_bind(struct device *dev) static void meson_drv_unbind(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); + struct meson_drm *priv = drm->dev_private; + + if (priv->canvas) { + meson_canvas_free(priv->canvas, priv->canvas_id_osd1); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_0); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_1); + meson_canvas_free(priv->canvas, priv->canvas_id_vd1_2); + } drm_dev_unregister(drm); drm_kms_helper_poll_fini(drm); diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h index aab96260da9f..4dccf4cd042a 100644 --- a/drivers/gpu/drm/meson/meson_drv.h +++ b/drivers/gpu/drm/meson/meson_drv.h @@ -22,6 +22,7 @@ #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/of.h> +#include <linux/soc/amlogic/meson-canvas.h> #include <drm/drmP.h> struct meson_drm { @@ -31,9 +32,16 @@ struct meson_drm { struct regmap *dmc; int vsync_irq; + struct meson_canvas *canvas; + u8 canvas_id_osd1; + u8 canvas_id_vd1_0; + u8 canvas_id_vd1_1; + u8 canvas_id_vd1_2; + struct drm_device *drm; struct drm_crtc *crtc; struct drm_plane *primary_plane; + struct drm_plane *overlay_plane; /* Components Data */ struct { @@ -45,6 +53,64 @@ struct meson_drm { uint32_t osd1_addr; uint32_t osd1_stride; uint32_t osd1_height; + uint32_t osd_sc_ctrl0; + uint32_t osd_sc_i_wh_m1; + uint32_t osd_sc_o_h_start_end; + uint32_t osd_sc_o_v_start_end; + uint32_t osd_sc_v_ini_phase; + uint32_t osd_sc_v_phase_step; + uint32_t osd_sc_h_ini_phase; + uint32_t osd_sc_h_phase_step; + uint32_t osd_sc_h_ctrl0; + uint32_t osd_sc_v_ctrl0; + + bool vd1_enabled; + bool vd1_commit; + unsigned int vd1_planes; + uint32_t vd1_if0_gen_reg; + uint32_t vd1_if0_luma_x0; + uint32_t vd1_if0_luma_y0; + uint32_t vd1_if0_chroma_x0; + uint32_t vd1_if0_chroma_y0; + uint32_t vd1_if0_repeat_loop; + uint32_t vd1_if0_luma0_rpt_pat; + uint32_t vd1_if0_chroma0_rpt_pat; + uint32_t vd1_range_map_y; + uint32_t vd1_range_map_cb; + uint32_t vd1_range_map_cr; + uint32_t viu_vd1_fmt_w; + uint32_t vd1_if0_canvas0; + uint32_t vd1_if0_gen_reg2; + uint32_t viu_vd1_fmt_ctrl; + uint32_t vd1_addr0; + uint32_t vd1_addr1; + uint32_t vd1_addr2; + uint32_t vd1_stride0; + uint32_t vd1_stride1; + uint32_t vd1_stride2; + uint32_t vd1_height0; + uint32_t vd1_height1; + uint32_t vd1_height2; + uint32_t vpp_pic_in_height; + uint32_t vpp_postblend_vd1_h_start_end; + uint32_t vpp_postblend_vd1_v_start_end; + uint32_t vpp_hsc_region12_startp; + uint32_t vpp_hsc_region34_startp; + uint32_t vpp_hsc_region4_endp; + uint32_t vpp_hsc_start_phase_step; + uint32_t vpp_hsc_region1_phase_slope; + uint32_t vpp_hsc_region3_phase_slope; + uint32_t vpp_line_in_length; + uint32_t vpp_preblend_h_size; + uint32_t vpp_vsc_region12_startp; + uint32_t vpp_vsc_region34_startp; + uint32_t vpp_vsc_region4_endp; + uint32_t vpp_vsc_start_phase_step; + uint32_t vpp_vsc_ini_phase; + uint32_t vpp_vsc_phase_ctrl; + uint32_t vpp_hsc_phase_ctrl; + uint32_t vpp_blend_vd2_h_start_end; + uint32_t vpp_blend_vd2_v_start_end; } viu; struct { diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c index df7247cd93f9..d8c5cc34e22e 100644 --- a/drivers/gpu/drm/meson/meson_dw_hdmi.c +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c @@ -594,17 +594,7 @@ dw_hdmi_mode_valid(struct drm_connector *connector, dev_dbg(connector->dev->dev, "%s: vclk:%d venc=%d hdmi=%d\n", __func__, vclk_freq, venc_freq, hdmi_freq); - /* Finally filter by configurable vclk frequencies for VIC modes */ - switch (vclk_freq) { - case 54000: - case 74250: - case 148500: - case 297000: - case 594000: - return MODE_OK; - } - - return MODE_CLOCK_RANGE; + return meson_vclk_vic_supported_freq(vclk_freq); } /* Encoder */ diff --git a/drivers/gpu/drm/meson/meson_overlay.c b/drivers/gpu/drm/meson/meson_overlay.c new file mode 100644 index 000000000000..691a9fd16b36 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_overlay.c @@ -0,0 +1,588 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/bitfield.h> +#include <linux/platform_device.h> +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_rect.h> + +#include "meson_overlay.h" +#include "meson_vpp.h" +#include "meson_viu.h" +#include "meson_canvas.h" +#include "meson_registers.h" + +/* VD1_IF0_GEN_REG */ +#define VD_URGENT_CHROMA BIT(28) +#define VD_URGENT_LUMA BIT(27) +#define VD_HOLD_LINES(lines) FIELD_PREP(GENMASK(24, 19), lines) +#define VD_DEMUX_MODE_RGB BIT(16) +#define VD_BYTES_PER_PIXEL(val) FIELD_PREP(GENMASK(15, 14), val) +#define VD_CHRO_RPT_LASTL_CTRL BIT(6) +#define VD_LITTLE_ENDIAN BIT(4) +#define VD_SEPARATE_EN BIT(1) +#define VD_ENABLE BIT(0) + +/* VD1_IF0_CANVAS0 */ +#define CANVAS_ADDR2(addr) FIELD_PREP(GENMASK(23, 16), addr) +#define CANVAS_ADDR1(addr) FIELD_PREP(GENMASK(15, 8), addr) +#define CANVAS_ADDR0(addr) FIELD_PREP(GENMASK(7, 0), addr) + +/* VD1_IF0_LUMA_X0 VD1_IF0_CHROMA_X0 */ +#define VD_X_START(value) FIELD_PREP(GENMASK(14, 0), value) +#define VD_X_END(value) FIELD_PREP(GENMASK(30, 16), value) + +/* VD1_IF0_LUMA_Y0 VD1_IF0_CHROMA_Y0 */ +#define VD_Y_START(value) FIELD_PREP(GENMASK(12, 0), value) +#define VD_Y_END(value) FIELD_PREP(GENMASK(28, 16), value) + +/* VD1_IF0_GEN_REG2 */ +#define VD_COLOR_MAP(value) FIELD_PREP(GENMASK(1, 0), value) + +/* VIU_VD1_FMT_CTRL */ +#define VD_HORZ_Y_C_RATIO(value) FIELD_PREP(GENMASK(22, 21), value) +#define VD_HORZ_FMT_EN BIT(20) +#define VD_VERT_RPT_LINE0 BIT(16) +#define VD_VERT_INITIAL_PHASE(value) FIELD_PREP(GENMASK(11, 8), value) +#define VD_VERT_PHASE_STEP(value) FIELD_PREP(GENMASK(7, 1), value) +#define VD_VERT_FMT_EN BIT(0) + +/* VPP_POSTBLEND_VD1_H_START_END */ +#define VD_H_END(value) FIELD_PREP(GENMASK(11, 0), value) +#define VD_H_START(value) FIELD_PREP(GENMASK(27, 16), value) + +/* VPP_POSTBLEND_VD1_V_START_END */ +#define VD_V_END(value) FIELD_PREP(GENMASK(11, 0), value) +#define VD_V_START(value) FIELD_PREP(GENMASK(27, 16), value) + +/* VPP_BLEND_VD2_V_START_END */ +#define VD2_V_END(value) FIELD_PREP(GENMASK(11, 0), value) +#define VD2_V_START(value) FIELD_PREP(GENMASK(27, 16), value) + +/* VIU_VD1_FMT_W */ +#define VD_V_WIDTH(value) FIELD_PREP(GENMASK(11, 0), value) +#define VD_H_WIDTH(value) FIELD_PREP(GENMASK(27, 16), value) + +/* VPP_HSC_REGION12_STARTP VPP_HSC_REGION34_STARTP */ +#define VD_REGION24_START(value) FIELD_PREP(GENMASK(11, 0), value) +#define VD_REGION13_END(value) FIELD_PREP(GENMASK(27, 16), value) + +struct meson_overlay { + struct drm_plane base; + struct meson_drm *priv; +}; +#define to_meson_overlay(x) container_of(x, struct meson_overlay, base) + +#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) + +static int meson_overlay_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_crtc_state *crtc_state; + + if (!state->crtc) + return 0; + + crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + return drm_atomic_helper_check_plane_state(state, crtc_state, + FRAC_16_16(1, 5), + FRAC_16_16(5, 1), + true, true); +} + +/* Takes a fixed 16.16 number and converts it to integer. */ +static inline int64_t fixed16_to_int(int64_t value) +{ + return value >> 16; +} + +static const uint8_t skip_tab[6] = { + 0x24, 0x04, 0x68, 0x48, 0x28, 0x08, +}; + +static void meson_overlay_get_vertical_phase(unsigned int ratio_y, int *phase, + int *repeat, bool interlace) +{ + int offset_in = 0; + int offset_out = 0; + int repeat_skip = 0; + + if (!interlace && ratio_y > (1 << 18)) + offset_out = (1 * ratio_y) >> 10; + + while ((offset_in + (4 << 8)) <= offset_out) { + repeat_skip++; + offset_in += 4 << 8; + } + + *phase = (offset_out - offset_in) >> 2; + + if (*phase > 0x100) + repeat_skip++; + + *phase = *phase & 0xff; + + if (repeat_skip > 5) + repeat_skip = 5; + + *repeat = skip_tab[repeat_skip]; +} + +static void meson_overlay_setup_scaler_params(struct meson_drm *priv, + struct drm_plane *plane, + bool interlace_mode) +{ + struct drm_crtc_state *crtc_state = priv->crtc->state; + int video_top, video_left, video_width, video_height; + struct drm_plane_state *state = plane->state; + unsigned int vd_start_lines, vd_end_lines; + unsigned int hd_start_lines, hd_end_lines; + unsigned int crtc_height, crtc_width; + unsigned int vsc_startp, vsc_endp; + unsigned int hsc_startp, hsc_endp; + unsigned int crop_top, crop_left; + int vphase, vphase_repeat_skip; + unsigned int ratio_x, ratio_y; + int temp_height, temp_width; + unsigned int w_in, h_in; + int temp, start, end; + + if (!crtc_state) { + DRM_ERROR("Invalid crtc_state\n"); + return; + } + + crtc_height = crtc_state->mode.vdisplay; + crtc_width = crtc_state->mode.hdisplay; + + w_in = fixed16_to_int(state->src_w); + h_in = fixed16_to_int(state->src_h); + crop_top = fixed16_to_int(state->src_x); + crop_left = fixed16_to_int(state->src_x); + + video_top = state->crtc_y; + video_left = state->crtc_x; + video_width = state->crtc_w; + video_height = state->crtc_h; + + DRM_DEBUG("crtc_width %d crtc_height %d interlace %d\n", + crtc_width, crtc_height, interlace_mode); + DRM_DEBUG("w_in %d h_in %d crop_top %d crop_left %d\n", + w_in, h_in, crop_top, crop_left); + DRM_DEBUG("video top %d left %d width %d height %d\n", + video_top, video_left, video_width, video_height); + + ratio_x = (w_in << 18) / video_width; + ratio_y = (h_in << 18) / video_height; + + if (ratio_x * video_width < (w_in << 18)) + ratio_x++; + + DRM_DEBUG("ratio x 0x%x y 0x%x\n", ratio_x, ratio_y); + + meson_overlay_get_vertical_phase(ratio_y, &vphase, &vphase_repeat_skip, + interlace_mode); + + DRM_DEBUG("vphase 0x%x skip %d\n", vphase, vphase_repeat_skip); + + /* Vertical */ + + start = video_top + video_height / 2 - ((h_in << 17) / ratio_y); + end = (h_in << 18) / ratio_y + start - 1; + + if (video_top < 0 && start < 0) + vd_start_lines = (-(start) * ratio_y) >> 18; + else if (start < video_top) + vd_start_lines = ((video_top - start) * ratio_y) >> 18; + else + vd_start_lines = 0; + + if (video_top < 0) + temp_height = min_t(unsigned int, + video_top + video_height - 1, + crtc_height - 1); + else + temp_height = min_t(unsigned int, + video_top + video_height - 1, + crtc_height - 1) - video_top + 1; + + temp = vd_start_lines + (temp_height * ratio_y >> 18); + vd_end_lines = (temp <= (h_in - 1)) ? temp : (h_in - 1); + + vd_start_lines += crop_left; + vd_end_lines += crop_left; + + /* + * TOFIX: Input frames are handled and scaled like progressive frames, + * proper handling of interlaced field input frames need to be figured + * out using the proper framebuffer flags set by userspace. + */ + if (interlace_mode) { + start >>= 1; + end >>= 1; + } + + vsc_startp = max_t(int, start, + max_t(int, 0, video_top)); + vsc_endp = min_t(int, end, + min_t(int, crtc_height - 1, + video_top + video_height - 1)); + + DRM_DEBUG("vsc startp %d endp %d start_lines %d end_lines %d\n", + vsc_startp, vsc_endp, vd_start_lines, vd_end_lines); + + /* Horizontal */ + + start = video_left + video_width / 2 - ((w_in << 17) / ratio_x); + end = (w_in << 18) / ratio_x + start - 1; + + if (video_left < 0 && start < 0) + hd_start_lines = (-(start) * ratio_x) >> 18; + else if (start < video_left) + hd_start_lines = ((video_left - start) * ratio_x) >> 18; + else + hd_start_lines = 0; + + if (video_left < 0) + temp_width = min_t(unsigned int, + video_left + video_width - 1, + crtc_width - 1); + else + temp_width = min_t(unsigned int, + video_left + video_width - 1, + crtc_width - 1) - video_left + 1; + + temp = hd_start_lines + (temp_width * ratio_x >> 18); + hd_end_lines = (temp <= (w_in - 1)) ? temp : (w_in - 1); + + priv->viu.vpp_line_in_length = hd_end_lines - hd_start_lines + 1; + hsc_startp = max_t(int, start, max_t(int, 0, video_left)); + hsc_endp = min_t(int, end, min_t(int, crtc_width - 1, + video_left + video_width - 1)); + + hd_start_lines += crop_top; + hd_end_lines += crop_top; + + DRM_DEBUG("hsc startp %d endp %d start_lines %d end_lines %d\n", + hsc_startp, hsc_endp, hd_start_lines, hd_end_lines); + + priv->viu.vpp_vsc_start_phase_step = ratio_y << 6; + + priv->viu.vpp_vsc_ini_phase = vphase << 8; + priv->viu.vpp_vsc_phase_ctrl = (1 << 13) | (4 << 8) | + vphase_repeat_skip; + + priv->viu.vd1_if0_luma_x0 = VD_X_START(hd_start_lines) | + VD_X_END(hd_end_lines); + priv->viu.vd1_if0_chroma_x0 = VD_X_START(hd_start_lines >> 1) | + VD_X_END(hd_end_lines >> 1); + + priv->viu.viu_vd1_fmt_w = + VD_H_WIDTH(hd_end_lines - hd_start_lines + 1) | + VD_V_WIDTH(hd_end_lines/2 - hd_start_lines/2 + 1); + + priv->viu.vd1_if0_luma_y0 = VD_Y_START(vd_start_lines) | + VD_Y_END(vd_end_lines); + + priv->viu.vd1_if0_chroma_y0 = VD_Y_START(vd_start_lines >> 1) | + VD_Y_END(vd_end_lines >> 1); + + priv->viu.vpp_pic_in_height = h_in; + + priv->viu.vpp_postblend_vd1_h_start_end = VD_H_START(hsc_startp) | + VD_H_END(hsc_endp); + priv->viu.vpp_blend_vd2_h_start_end = VD_H_START(hd_start_lines) | + VD_H_END(hd_end_lines); + priv->viu.vpp_hsc_region12_startp = VD_REGION13_END(0) | + VD_REGION24_START(hsc_startp); + priv->viu.vpp_hsc_region34_startp = + VD_REGION13_END(hsc_startp) | + VD_REGION24_START(hsc_endp - hsc_startp); + priv->viu.vpp_hsc_region4_endp = hsc_endp - hsc_startp; + priv->viu.vpp_hsc_start_phase_step = ratio_x << 6; + priv->viu.vpp_hsc_region1_phase_slope = 0; + priv->viu.vpp_hsc_region3_phase_slope = 0; + priv->viu.vpp_hsc_phase_ctrl = (1 << 21) | (4 << 16); + + priv->viu.vpp_line_in_length = hd_end_lines - hd_start_lines + 1; + priv->viu.vpp_preblend_h_size = hd_end_lines - hd_start_lines + 1; + + priv->viu.vpp_postblend_vd1_v_start_end = VD_V_START(vsc_startp) | + VD_V_END(vsc_endp); + priv->viu.vpp_blend_vd2_v_start_end = + VD2_V_START((vd_end_lines + 1) >> 1) | + VD2_V_END(vd_end_lines); + + priv->viu.vpp_vsc_region12_startp = 0; + priv->viu.vpp_vsc_region34_startp = + VD_REGION13_END(vsc_endp - vsc_startp) | + VD_REGION24_START(vsc_endp - vsc_startp); + priv->viu.vpp_vsc_region4_endp = vsc_endp - vsc_startp; + priv->viu.vpp_vsc_start_phase_step = ratio_y << 6; +} + +static void meson_overlay_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct meson_overlay *meson_overlay = to_meson_overlay(plane); + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + struct meson_drm *priv = meson_overlay->priv; + struct drm_gem_cma_object *gem; + unsigned long flags; + bool interlace_mode; + + DRM_DEBUG_DRIVER("\n"); + + /* Fallback is canvas provider is not available */ + if (!priv->canvas) { + priv->canvas_id_vd1_0 = MESON_CANVAS_ID_VD1_0; + priv->canvas_id_vd1_1 = MESON_CANVAS_ID_VD1_1; + priv->canvas_id_vd1_2 = MESON_CANVAS_ID_VD1_2; + } + + interlace_mode = state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE; + + spin_lock_irqsave(&priv->drm->event_lock, flags); + + priv->viu.vd1_if0_gen_reg = VD_URGENT_CHROMA | + VD_URGENT_LUMA | + VD_HOLD_LINES(9) | + VD_CHRO_RPT_LASTL_CTRL | + VD_ENABLE; + + /* Setup scaler params */ + meson_overlay_setup_scaler_params(priv, plane, interlace_mode); + + priv->viu.vd1_if0_repeat_loop = 0; + priv->viu.vd1_if0_luma0_rpt_pat = interlace_mode ? 8 : 0; + priv->viu.vd1_if0_chroma0_rpt_pat = interlace_mode ? 8 : 0; + priv->viu.vd1_range_map_y = 0; + priv->viu.vd1_range_map_cb = 0; + priv->viu.vd1_range_map_cr = 0; + + /* Default values for RGB888/YUV444 */ + priv->viu.vd1_if0_gen_reg2 = 0; + priv->viu.viu_vd1_fmt_ctrl = 0; + + switch (fb->format->format) { + /* TOFIX DRM_FORMAT_RGB888 should be supported */ + case DRM_FORMAT_YUYV: + priv->viu.vd1_if0_gen_reg |= VD_BYTES_PER_PIXEL(1); + priv->viu.vd1_if0_canvas0 = + CANVAS_ADDR2(priv->canvas_id_vd1_0) | + CANVAS_ADDR1(priv->canvas_id_vd1_0) | + CANVAS_ADDR0(priv->canvas_id_vd1_0); + priv->viu.viu_vd1_fmt_ctrl = VD_HORZ_Y_C_RATIO(1) | /* /2 */ + VD_HORZ_FMT_EN | + VD_VERT_RPT_LINE0 | + VD_VERT_INITIAL_PHASE(12) | + VD_VERT_PHASE_STEP(16) | /* /2 */ + VD_VERT_FMT_EN; + break; + case DRM_FORMAT_NV12: + case DRM_FORMAT_NV21: + priv->viu.vd1_if0_gen_reg |= VD_SEPARATE_EN; + priv->viu.vd1_if0_canvas0 = + CANVAS_ADDR2(priv->canvas_id_vd1_1) | + CANVAS_ADDR1(priv->canvas_id_vd1_1) | + CANVAS_ADDR0(priv->canvas_id_vd1_0); + if (fb->format->format == DRM_FORMAT_NV12) + priv->viu.vd1_if0_gen_reg2 = VD_COLOR_MAP(1); + else + priv->viu.vd1_if0_gen_reg2 = VD_COLOR_MAP(2); + priv->viu.viu_vd1_fmt_ctrl = VD_HORZ_Y_C_RATIO(1) | /* /2 */ + VD_HORZ_FMT_EN | + VD_VERT_RPT_LINE0 | + VD_VERT_INITIAL_PHASE(12) | + VD_VERT_PHASE_STEP(8) | /* /4 */ + VD_VERT_FMT_EN; + break; + case DRM_FORMAT_YUV444: + case DRM_FORMAT_YUV422: + case DRM_FORMAT_YUV420: + case DRM_FORMAT_YUV411: + case DRM_FORMAT_YUV410: + priv->viu.vd1_if0_gen_reg |= VD_SEPARATE_EN; + priv->viu.vd1_if0_canvas0 = + CANVAS_ADDR2(priv->canvas_id_vd1_2) | + CANVAS_ADDR1(priv->canvas_id_vd1_1) | + CANVAS_ADDR0(priv->canvas_id_vd1_0); + switch (fb->format->format) { + case DRM_FORMAT_YUV422: + priv->viu.viu_vd1_fmt_ctrl = + VD_HORZ_Y_C_RATIO(1) | /* /2 */ + VD_HORZ_FMT_EN | + VD_VERT_RPT_LINE0 | + VD_VERT_INITIAL_PHASE(12) | + VD_VERT_PHASE_STEP(16) | /* /2 */ + VD_VERT_FMT_EN; + break; + case DRM_FORMAT_YUV420: + priv->viu.viu_vd1_fmt_ctrl = + VD_HORZ_Y_C_RATIO(1) | /* /2 */ + VD_HORZ_FMT_EN | + VD_VERT_RPT_LINE0 | + VD_VERT_INITIAL_PHASE(12) | + VD_VERT_PHASE_STEP(8) | /* /4 */ + VD_VERT_FMT_EN; + break; + case DRM_FORMAT_YUV411: + priv->viu.viu_vd1_fmt_ctrl = + VD_HORZ_Y_C_RATIO(2) | /* /4 */ + VD_HORZ_FMT_EN | + VD_VERT_RPT_LINE0 | + VD_VERT_INITIAL_PHASE(12) | + VD_VERT_PHASE_STEP(16) | /* /2 */ + VD_VERT_FMT_EN; + break; + case DRM_FORMAT_YUV410: + priv->viu.viu_vd1_fmt_ctrl = + VD_HORZ_Y_C_RATIO(2) | /* /4 */ + VD_HORZ_FMT_EN | + VD_VERT_RPT_LINE0 | + VD_VERT_INITIAL_PHASE(12) | + VD_VERT_PHASE_STEP(8) | /* /4 */ + VD_VERT_FMT_EN; + break; + } + break; + } + + /* Update Canvas with buffer address */ + priv->viu.vd1_planes = drm_format_num_planes(fb->format->format); + + switch (priv->viu.vd1_planes) { + case 3: + gem = drm_fb_cma_get_gem_obj(fb, 2); + priv->viu.vd1_addr2 = gem->paddr + fb->offsets[2]; + priv->viu.vd1_stride2 = fb->pitches[2]; + priv->viu.vd1_height2 = + drm_format_plane_height(fb->height, + fb->format->format, 2); + DRM_DEBUG("plane 2 addr 0x%x stride %d height %d\n", + priv->viu.vd1_addr2, + priv->viu.vd1_stride2, + priv->viu.vd1_height2); + /* fallthrough */ + case 2: + gem = drm_fb_cma_get_gem_obj(fb, 1); + priv->viu.vd1_addr1 = gem->paddr + fb->offsets[1]; + priv->viu.vd1_stride1 = fb->pitches[1]; + priv->viu.vd1_height1 = + drm_format_plane_height(fb->height, + fb->format->format, 1); + DRM_DEBUG("plane 1 addr 0x%x stride %d height %d\n", + priv->viu.vd1_addr1, + priv->viu.vd1_stride1, + priv->viu.vd1_height1); + /* fallthrough */ + case 1: + gem = drm_fb_cma_get_gem_obj(fb, 0); + priv->viu.vd1_addr0 = gem->paddr + fb->offsets[0]; + priv->viu.vd1_stride0 = fb->pitches[0]; + priv->viu.vd1_height0 = + drm_format_plane_height(fb->height, + fb->format->format, 0); + DRM_DEBUG("plane 0 addr 0x%x stride %d height %d\n", + priv->viu.vd1_addr0, + priv->viu.vd1_stride0, + priv->viu.vd1_height0); + } + + priv->viu.vd1_enabled = true; + + spin_unlock_irqrestore(&priv->drm->event_lock, flags); + + DRM_DEBUG_DRIVER("\n"); +} + +static void meson_overlay_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct meson_overlay *meson_overlay = to_meson_overlay(plane); + struct meson_drm *priv = meson_overlay->priv; + + DRM_DEBUG_DRIVER("\n"); + + priv->viu.vd1_enabled = false; + + /* Disable VD1 */ + writel_bits_relaxed(VPP_VD1_POSTBLEND | VPP_VD1_PREBLEND, 0, + priv->io_base + _REG(VPP_MISC)); + +} + +static const struct drm_plane_helper_funcs meson_overlay_helper_funcs = { + .atomic_check = meson_overlay_atomic_check, + .atomic_disable = meson_overlay_atomic_disable, + .atomic_update = meson_overlay_atomic_update, + .prepare_fb = drm_gem_fb_prepare_fb, +}; + +static const struct drm_plane_funcs meson_overlay_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = drm_plane_cleanup, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static const uint32_t supported_drm_formats[] = { + DRM_FORMAT_YUYV, + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, + DRM_FORMAT_YUV444, + DRM_FORMAT_YUV422, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV411, + DRM_FORMAT_YUV410, +}; + +int meson_overlay_create(struct meson_drm *priv) +{ + struct meson_overlay *meson_overlay; + struct drm_plane *plane; + + DRM_DEBUG_DRIVER("\n"); + + meson_overlay = devm_kzalloc(priv->drm->dev, sizeof(*meson_overlay), + GFP_KERNEL); + if (!meson_overlay) + return -ENOMEM; + + meson_overlay->priv = priv; + plane = &meson_overlay->base; + + drm_universal_plane_init(priv->drm, plane, 0xFF, + &meson_overlay_funcs, + supported_drm_formats, + ARRAY_SIZE(supported_drm_formats), + NULL, + DRM_PLANE_TYPE_OVERLAY, "meson_overlay_plane"); + + drm_plane_helper_add(plane, &meson_overlay_helper_funcs); + + priv->overlay_plane = plane; + + DRM_DEBUG_DRIVER("\n"); + + return 0; +} diff --git a/drivers/gpu/drm/meson/meson_overlay.h b/drivers/gpu/drm/meson/meson_overlay.h new file mode 100644 index 000000000000..dae24f5ac63d --- /dev/null +++ b/drivers/gpu/drm/meson/meson_overlay.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + */ + +#ifndef __MESON_OVERLAY_H +#define __MESON_OVERLAY_H + +#include "meson_drv.h" + +int meson_overlay_create(struct meson_drm *priv); + +#endif /* __MESON_OVERLAY_H */ diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c index 12c80dfcff59..6119a0224278 100644 --- a/drivers/gpu/drm/meson/meson_plane.c +++ b/drivers/gpu/drm/meson/meson_plane.c @@ -24,6 +24,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/bitfield.h> #include <linux/platform_device.h> #include <drm/drmP.h> #include <drm/drm_atomic.h> @@ -31,6 +32,7 @@ #include <drm/drm_plane_helper.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_rect.h> #include "meson_plane.h" @@ -39,12 +41,51 @@ #include "meson_canvas.h" #include "meson_registers.h" +/* OSD_SCI_WH_M1 */ +#define SCI_WH_M1_W(w) FIELD_PREP(GENMASK(28, 16), w) +#define SCI_WH_M1_H(h) FIELD_PREP(GENMASK(12, 0), h) + +/* OSD_SCO_H_START_END */ +/* OSD_SCO_V_START_END */ +#define SCO_HV_START(start) FIELD_PREP(GENMASK(27, 16), start) +#define SCO_HV_END(end) FIELD_PREP(GENMASK(11, 0), end) + +/* OSD_SC_CTRL0 */ +#define SC_CTRL0_PATH_EN BIT(3) +#define SC_CTRL0_SEL_OSD1 BIT(2) + +/* OSD_VSC_CTRL0 */ +#define VSC_BANK_LEN(value) FIELD_PREP(GENMASK(2, 0), value) +#define VSC_TOP_INI_RCV_NUM(value) FIELD_PREP(GENMASK(6, 3), value) +#define VSC_TOP_RPT_L0_NUM(value) FIELD_PREP(GENMASK(9, 8), value) +#define VSC_BOT_INI_RCV_NUM(value) FIELD_PREP(GENMASK(14, 11), value) +#define VSC_BOT_RPT_L0_NUM(value) FIELD_PREP(GENMASK(17, 16), value) +#define VSC_PROG_INTERLACE BIT(23) +#define VSC_VERTICAL_SCALER_EN BIT(24) + +/* OSD_VSC_INI_PHASE */ +#define VSC_INI_PHASE_BOT(bottom) FIELD_PREP(GENMASK(31, 16), bottom) +#define VSC_INI_PHASE_TOP(top) FIELD_PREP(GENMASK(15, 0), top) + +/* OSD_HSC_CTRL0 */ +#define HSC_BANK_LENGTH(value) FIELD_PREP(GENMASK(2, 0), value) +#define HSC_INI_RCV_NUM0(value) FIELD_PREP(GENMASK(6, 3), value) +#define HSC_RPT_P0_NUM0(value) FIELD_PREP(GENMASK(9, 8), value) +#define HSC_HORIZ_SCALER_EN BIT(22) + +/* VPP_OSD_VSC_PHASE_STEP */ +/* VPP_OSD_HSC_PHASE_STEP */ +#define SC_PHASE_STEP(value) FIELD_PREP(GENMASK(27, 0), value) + struct meson_plane { struct drm_plane base; struct meson_drm *priv; + bool enabled; }; #define to_meson_plane(x) container_of(x, struct meson_plane, base) +#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) + static int meson_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { @@ -57,10 +98,15 @@ static int meson_plane_atomic_check(struct drm_plane *plane, if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state); + /* + * Only allow : + * - Upscaling up to 5x, vertical and horizontal + * - Final coordinates must match crtc size + */ return drm_atomic_helper_check_plane_state(state, crtc_state, + FRAC_16_16(1, 5), DRM_PLANE_HELPER_NO_SCALING, - DRM_PLANE_HELPER_NO_SCALING, - true, true); + false, true); } /* Takes a fixed 16.16 number and converts it to integer. */ @@ -74,22 +120,20 @@ static void meson_plane_atomic_update(struct drm_plane *plane, { struct meson_plane *meson_plane = to_meson_plane(plane); struct drm_plane_state *state = plane->state; - struct drm_framebuffer *fb = state->fb; + struct drm_rect dest = drm_plane_state_dest(state); struct meson_drm *priv = meson_plane->priv; + struct drm_framebuffer *fb = state->fb; struct drm_gem_cma_object *gem; - struct drm_rect src = { - .x1 = (state->src_x), - .y1 = (state->src_y), - .x2 = (state->src_x + state->src_w), - .y2 = (state->src_y + state->src_h), - }; - struct drm_rect dest = { - .x1 = state->crtc_x, - .y1 = state->crtc_y, - .x2 = state->crtc_x + state->crtc_w, - .y2 = state->crtc_y + state->crtc_h, - }; unsigned long flags; + int vsc_ini_rcv_num, vsc_ini_rpt_p0_num; + int vsc_bot_rcv_num, vsc_bot_rpt_p0_num; + int hsc_ini_rcv_num, hsc_ini_rpt_p0_num; + int hf_phase_step, vf_phase_step; + int src_w, src_h, dst_w, dst_h; + int bot_ini_phase; + int hf_bank_len; + int vf_bank_len; + u8 canvas_id_osd1; /* * Update Coordinates @@ -104,8 +148,13 @@ static void meson_plane_atomic_update(struct drm_plane *plane, (0xFF << OSD_GLOBAL_ALPHA_SHIFT) | OSD_BLK0_ENABLE; + if (priv->canvas) + canvas_id_osd1 = priv->canvas_id_osd1; + else + canvas_id_osd1 = MESON_CANVAS_ID_OSD1; + /* Set up BLK0 to point to the right canvas */ - priv->viu.osd1_blk0_cfg[0] = ((MESON_CANVAS_ID_OSD1 << OSD_CANVAS_SEL) | + priv->viu.osd1_blk0_cfg[0] = ((canvas_id_osd1 << OSD_CANVAS_SEL) | OSD_ENDIANNESS_LE); /* On GXBB, Use the old non-HDR RGB2YUV converter */ @@ -137,23 +186,115 @@ static void meson_plane_atomic_update(struct drm_plane *plane, break; }; + /* Default scaler parameters */ + vsc_bot_rcv_num = 0; + vsc_bot_rpt_p0_num = 0; + hf_bank_len = 4; + vf_bank_len = 4; + if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) { - priv->viu.osd1_interlace = true; + vsc_bot_rcv_num = 6; + vsc_bot_rpt_p0_num = 2; + } + + hsc_ini_rcv_num = hf_bank_len; + vsc_ini_rcv_num = vf_bank_len; + hsc_ini_rpt_p0_num = (hf_bank_len / 2) - 1; + vsc_ini_rpt_p0_num = (vf_bank_len / 2) - 1; + + src_w = fixed16_to_int(state->src_w); + src_h = fixed16_to_int(state->src_h); + dst_w = state->crtc_w; + dst_h = state->crtc_h; + /* + * When the output is interlaced, the OSD must switch between + * each field using the INTERLACE_SEL_ODD (0) of VIU_OSD1_BLK0_CFG_W0 + * at each vsync. + * But the vertical scaler can provide such funtionnality if + * is configured for 2:1 scaling with interlace options enabled. + */ + if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) { dest.y1 /= 2; dest.y2 /= 2; - } else - priv->viu.osd1_interlace = false; + dst_h /= 2; + } + + hf_phase_step = ((src_w << 18) / dst_w) << 6; + vf_phase_step = (src_h << 20) / dst_h; + + if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) + bot_ini_phase = ((vf_phase_step / 2) >> 4); + else + bot_ini_phase = 0; + + vf_phase_step = (vf_phase_step << 4); + + /* In interlaced mode, scaler is always active */ + if (src_h != dst_h || src_w != dst_w) { + priv->viu.osd_sc_i_wh_m1 = SCI_WH_M1_W(src_w - 1) | + SCI_WH_M1_H(src_h - 1); + priv->viu.osd_sc_o_h_start_end = SCO_HV_START(dest.x1) | + SCO_HV_END(dest.x2 - 1); + priv->viu.osd_sc_o_v_start_end = SCO_HV_START(dest.y1) | + SCO_HV_END(dest.y2 - 1); + /* Enable OSD Scaler */ + priv->viu.osd_sc_ctrl0 = SC_CTRL0_PATH_EN | SC_CTRL0_SEL_OSD1; + } else { + priv->viu.osd_sc_i_wh_m1 = 0; + priv->viu.osd_sc_o_h_start_end = 0; + priv->viu.osd_sc_o_v_start_end = 0; + priv->viu.osd_sc_ctrl0 = 0; + } + + /* In interlaced mode, vertical scaler is always active */ + if (src_h != dst_h) { + priv->viu.osd_sc_v_ctrl0 = + VSC_BANK_LEN(vf_bank_len) | + VSC_TOP_INI_RCV_NUM(vsc_ini_rcv_num) | + VSC_TOP_RPT_L0_NUM(vsc_ini_rpt_p0_num) | + VSC_VERTICAL_SCALER_EN; + + if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) + priv->viu.osd_sc_v_ctrl0 |= + VSC_BOT_INI_RCV_NUM(vsc_bot_rcv_num) | + VSC_BOT_RPT_L0_NUM(vsc_bot_rpt_p0_num) | + VSC_PROG_INTERLACE; + + priv->viu.osd_sc_v_phase_step = SC_PHASE_STEP(vf_phase_step); + priv->viu.osd_sc_v_ini_phase = VSC_INI_PHASE_BOT(bot_ini_phase); + } else { + priv->viu.osd_sc_v_ctrl0 = 0; + priv->viu.osd_sc_v_phase_step = 0; + priv->viu.osd_sc_v_ini_phase = 0; + } + + /* Horizontal scaler is only used if width does not match */ + if (src_w != dst_w) { + priv->viu.osd_sc_h_ctrl0 = + HSC_BANK_LENGTH(hf_bank_len) | + HSC_INI_RCV_NUM0(hsc_ini_rcv_num) | + HSC_RPT_P0_NUM0(hsc_ini_rpt_p0_num) | + HSC_HORIZ_SCALER_EN; + priv->viu.osd_sc_h_phase_step = SC_PHASE_STEP(hf_phase_step); + priv->viu.osd_sc_h_ini_phase = 0; + } else { + priv->viu.osd_sc_h_ctrl0 = 0; + priv->viu.osd_sc_h_phase_step = 0; + priv->viu.osd_sc_h_ini_phase = 0; + } /* * The format of these registers is (x2 << 16 | x1), * where x2 is exclusive. * e.g. +30x1920 would be (1919 << 16) | 30 */ - priv->viu.osd1_blk0_cfg[1] = ((fixed16_to_int(src.x2) - 1) << 16) | - fixed16_to_int(src.x1); - priv->viu.osd1_blk0_cfg[2] = ((fixed16_to_int(src.y2) - 1) << 16) | - fixed16_to_int(src.y1); + priv->viu.osd1_blk0_cfg[1] = + ((fixed16_to_int(state->src.x2) - 1) << 16) | + fixed16_to_int(state->src.x1); + priv->viu.osd1_blk0_cfg[2] = + ((fixed16_to_int(state->src.y2) - 1) << 16) | + fixed16_to_int(state->src.y1); priv->viu.osd1_blk0_cfg[3] = ((dest.x2 - 1) << 16) | dest.x1; priv->viu.osd1_blk0_cfg[4] = ((dest.y2 - 1) << 16) | dest.y1; @@ -164,6 +305,15 @@ static void meson_plane_atomic_update(struct drm_plane *plane, priv->viu.osd1_stride = fb->pitches[0]; priv->viu.osd1_height = fb->height; + if (!meson_plane->enabled) { + /* Reset OSD1 before enabling it on GXL+ SoCs */ + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + meson_viu_osd1_reset(priv); + + meson_plane->enabled = true; + } + spin_unlock_irqrestore(&priv->drm->event_lock, flags); } @@ -177,12 +327,15 @@ static void meson_plane_atomic_disable(struct drm_plane *plane, writel_bits_relaxed(VPP_OSD1_POSTBLEND, 0, priv->io_base + _REG(VPP_MISC)); + meson_plane->enabled = false; + } static const struct drm_plane_helper_funcs meson_plane_helper_funcs = { .atomic_check = meson_plane_atomic_check, .atomic_disable = meson_plane_atomic_disable, .atomic_update = meson_plane_atomic_update, + .prepare_fb = drm_gem_fb_prepare_fb, }; static const struct drm_plane_funcs meson_plane_funcs = { diff --git a/drivers/gpu/drm/meson/meson_registers.h b/drivers/gpu/drm/meson/meson_registers.h index bca87143e548..5c7e02c703bc 100644 --- a/drivers/gpu/drm/meson/meson_registers.h +++ b/drivers/gpu/drm/meson/meson_registers.h @@ -286,6 +286,7 @@ #define VIU_OSD1_MATRIX_COEF22_30 0x1a9d #define VIU_OSD1_MATRIX_COEF31_32 0x1a9e #define VIU_OSD1_MATRIX_COEF40_41 0x1a9f +#define VD1_IF0_GEN_REG3 0x1aa7 #define VIU_OSD1_EOTF_CTL 0x1ad4 #define VIU_OSD1_EOTF_COEF00_01 0x1ad5 #define VIU_OSD1_EOTF_COEF02_10 0x1ad6 @@ -297,6 +298,7 @@ #define VIU_OSD1_OETF_CTL 0x1adc #define VIU_OSD1_OETF_LUT_ADDR_PORT 0x1add #define VIU_OSD1_OETF_LUT_DATA_PORT 0x1ade +#define AFBC_ENABLE 0x1ae0 /* vpp */ #define VPP_DUMMY_DATA 0x1d00 @@ -349,6 +351,7 @@ #define VPP_VD2_PREBLEND BIT(15) #define VPP_OSD1_PREBLEND BIT(16) #define VPP_OSD2_PREBLEND BIT(17) +#define VPP_COLOR_MNG_ENABLE BIT(28) #define VPP_OFIFO_SIZE 0x1d27 #define VPP_FIFO_STATUS 0x1d28 #define VPP_SMOKE_CTRL 0x1d29 diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c index ae5473257f72..f6ba35a405f8 100644 --- a/drivers/gpu/drm/meson/meson_vclk.c +++ b/drivers/gpu/drm/meson/meson_vclk.c @@ -117,6 +117,8 @@ #define HDMI_PLL_RESET BIT(28) #define HDMI_PLL_LOCK BIT(31) +#define FREQ_1000_1001(_freq) DIV_ROUND_CLOSEST(_freq * 1000, 1001) + /* VID PLL Dividers */ enum { VID_PLL_DIV_1 = 0, @@ -323,7 +325,7 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv) enum { /* PLL O1 O2 O3 VP DV EN TX */ /* 4320 /4 /4 /1 /5 /1 => /2 /2 */ - MESON_VCLK_HDMI_ENCI_54000 = 1, + MESON_VCLK_HDMI_ENCI_54000 = 0, /* 4320 /4 /4 /1 /5 /1 => /1 /2 */ MESON_VCLK_HDMI_DDR_54000, /* 2970 /4 /1 /1 /5 /1 => /1 /2 */ @@ -339,6 +341,7 @@ enum { }; struct meson_vclk_params { + unsigned int pixel_freq; unsigned int pll_base_freq; unsigned int pll_od1; unsigned int pll_od2; @@ -347,6 +350,7 @@ struct meson_vclk_params { unsigned int vclk_div; } params[] = { [MESON_VCLK_HDMI_ENCI_54000] = { + .pixel_freq = 54000, .pll_base_freq = 4320000, .pll_od1 = 4, .pll_od2 = 4, @@ -355,6 +359,7 @@ struct meson_vclk_params { .vclk_div = 1, }, [MESON_VCLK_HDMI_DDR_54000] = { + .pixel_freq = 54000, .pll_base_freq = 4320000, .pll_od1 = 4, .pll_od2 = 4, @@ -363,6 +368,7 @@ struct meson_vclk_params { .vclk_div = 1, }, [MESON_VCLK_HDMI_DDR_148500] = { + .pixel_freq = 148500, .pll_base_freq = 2970000, .pll_od1 = 4, .pll_od2 = 1, @@ -371,6 +377,7 @@ struct meson_vclk_params { .vclk_div = 1, }, [MESON_VCLK_HDMI_74250] = { + .pixel_freq = 74250, .pll_base_freq = 2970000, .pll_od1 = 2, .pll_od2 = 2, @@ -379,6 +386,7 @@ struct meson_vclk_params { .vclk_div = 1, }, [MESON_VCLK_HDMI_148500] = { + .pixel_freq = 148500, .pll_base_freq = 2970000, .pll_od1 = 1, .pll_od2 = 2, @@ -387,6 +395,7 @@ struct meson_vclk_params { .vclk_div = 1, }, [MESON_VCLK_HDMI_297000] = { + .pixel_freq = 297000, .pll_base_freq = 2970000, .pll_od1 = 1, .pll_od2 = 1, @@ -395,6 +404,7 @@ struct meson_vclk_params { .vclk_div = 2, }, [MESON_VCLK_HDMI_594000] = { + .pixel_freq = 594000, .pll_base_freq = 5940000, .pll_od1 = 1, .pll_od2 = 1, @@ -402,6 +412,7 @@ struct meson_vclk_params { .vid_pll_div = VID_PLL_DIV_5, .vclk_div = 1, }, + { /* sentinel */ }, }; static inline unsigned int pll_od_to_reg(unsigned int od) @@ -626,12 +637,37 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv, pll_freq); } +enum drm_mode_status +meson_vclk_vic_supported_freq(unsigned int freq) +{ + int i; + + DRM_DEBUG_DRIVER("freq = %d\n", freq); + + for (i = 0 ; params[i].pixel_freq ; ++i) { + DRM_DEBUG_DRIVER("i = %d pixel_freq = %d alt = %d\n", + i, params[i].pixel_freq, + FREQ_1000_1001(params[i].pixel_freq)); + /* Match strict frequency */ + if (freq == params[i].pixel_freq) + return MODE_OK; + /* Match 1000/1001 variant */ + if (freq == FREQ_1000_1001(params[i].pixel_freq)) + return MODE_OK; + } + + return MODE_CLOCK_RANGE; +} +EXPORT_SYMBOL_GPL(meson_vclk_vic_supported_freq); + static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq, unsigned int od1, unsigned int od2, unsigned int od3, unsigned int vid_pll_div, unsigned int vclk_div, unsigned int hdmi_tx_div, unsigned int venc_div, - bool hdmi_use_enci) + bool hdmi_use_enci, bool vic_alternate_clock) { + unsigned int m = 0, frac = 0; + /* Set HDMI-TX sys clock */ regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, CTS_HDMI_SYS_SEL_MASK, 0); @@ -646,34 +682,38 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq, } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) { switch (pll_base_freq) { case 2970000: - meson_hdmi_pll_set_params(priv, 0x3d, 0xe00, - od1, od2, od3); + m = 0x3d; + frac = vic_alternate_clock ? 0xd02 : 0xe00; break; case 4320000: - meson_hdmi_pll_set_params(priv, 0x5a, 0, - od1, od2, od3); + m = vic_alternate_clock ? 0x59 : 0x5a; + frac = vic_alternate_clock ? 0xe8f : 0; break; case 5940000: - meson_hdmi_pll_set_params(priv, 0x7b, 0xc00, - od1, od2, od3); + m = 0x7b; + frac = vic_alternate_clock ? 0xa05 : 0xc00; break; } + + meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3); } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) { switch (pll_base_freq) { case 2970000: - meson_hdmi_pll_set_params(priv, 0x7b, 0x300, - od1, od2, od3); + m = 0x7b; + frac = vic_alternate_clock ? 0x281 : 0x300; break; case 4320000: - meson_hdmi_pll_set_params(priv, 0xb4, 0, - od1, od2, od3); + m = vic_alternate_clock ? 0xb3 : 0xb4; + frac = vic_alternate_clock ? 0x347 : 0; break; case 5940000: - meson_hdmi_pll_set_params(priv, 0xf7, 0x200, - od1, od2, od3); + m = 0xf7; + frac = vic_alternate_clock ? 0x102 : 0x200; break; } + + meson_hdmi_pll_set_params(priv, m, frac, od1, od2, od3); } /* Setup vid_pll divider */ @@ -826,6 +866,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, unsigned int vclk_freq, unsigned int venc_freq, unsigned int dac_freq, bool hdmi_use_enci) { + bool vic_alternate_clock = false; unsigned int freq; unsigned int hdmi_tx_div; unsigned int venc_div; @@ -843,7 +884,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, * - encp encoder */ meson_vclk_set(priv, vclk_freq * 10, 0, 0, 0, - VID_PLL_DIV_5, 2, 1, 1, false); + VID_PLL_DIV_5, 2, 1, 1, false, false); return; } @@ -863,31 +904,35 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, return; } - switch (vclk_freq) { - case 54000: - if (hdmi_use_enci) - freq = MESON_VCLK_HDMI_ENCI_54000; - else - freq = MESON_VCLK_HDMI_DDR_54000; - break; - case 74250: - freq = MESON_VCLK_HDMI_74250; - break; - case 148500: - if (dac_freq != 148500) - freq = MESON_VCLK_HDMI_DDR_148500; - else - freq = MESON_VCLK_HDMI_148500; - break; - case 297000: - freq = MESON_VCLK_HDMI_297000; - break; - case 594000: - freq = MESON_VCLK_HDMI_594000; - break; - default: - pr_err("Fatal Error, invalid HDMI vclk freq %d\n", - vclk_freq); + for (freq = 0 ; params[freq].pixel_freq ; ++freq) { + if (vclk_freq == params[freq].pixel_freq || + vclk_freq == FREQ_1000_1001(params[freq].pixel_freq)) { + if (vclk_freq != params[freq].pixel_freq) + vic_alternate_clock = true; + else + vic_alternate_clock = false; + + if (freq == MESON_VCLK_HDMI_ENCI_54000 && + !hdmi_use_enci) + continue; + + if (freq == MESON_VCLK_HDMI_DDR_54000 && + hdmi_use_enci) + continue; + + if (freq == MESON_VCLK_HDMI_DDR_148500 && + dac_freq == vclk_freq) + continue; + + if (freq == MESON_VCLK_HDMI_148500 && + dac_freq != vclk_freq) + continue; + break; + } + } + + if (!params[freq].pixel_freq) { + pr_err("Fatal Error, invalid HDMI vclk freq %d\n", vclk_freq); return; } @@ -895,6 +940,6 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target, params[freq].pll_od1, params[freq].pll_od2, params[freq].pll_od3, params[freq].vid_pll_div, params[freq].vclk_div, hdmi_tx_div, venc_div, - hdmi_use_enci); + hdmi_use_enci, vic_alternate_clock); } EXPORT_SYMBOL_GPL(meson_vclk_setup); diff --git a/drivers/gpu/drm/meson/meson_vclk.h b/drivers/gpu/drm/meson/meson_vclk.h index 869fa3a3073e..4bd8752da02a 100644 --- a/drivers/gpu/drm/meson/meson_vclk.h +++ b/drivers/gpu/drm/meson/meson_vclk.h @@ -32,6 +32,8 @@ enum { enum drm_mode_status meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq); +enum drm_mode_status +meson_vclk_vic_supported_freq(unsigned int freq); void meson_vclk_setup(struct meson_drm *priv, unsigned int target, unsigned int vclk_freq, unsigned int venc_freq, diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c index acbbad3e322c..e95e0e7a7fa1 100644 --- a/drivers/gpu/drm/meson/meson_venc.c +++ b/drivers/gpu/drm/meson/meson_venc.c @@ -697,6 +697,132 @@ union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p60 = { }, }; +union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p24 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x8, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1000, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 140+3840, + .max_pxcnt = 3840+1660-1, + .hspuls_begin = 2156+1920, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059+1920, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 3987, + .vavon_bline = 89, + .vavon_eline = 2248, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156+1920, + .vso_begin = 2100+1920, + .vso_end = 2164+1920, + .vso_bline = 51, + .vso_eline = 53, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 2249, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p25 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x8, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1000, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 140+3840, + .max_pxcnt = 3840+1440-1, + .hspuls_begin = 2156+1920, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059+1920, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 3987, + .vavon_bline = 89, + .vavon_eline = 2248, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156+1920, + .vso_begin = 2100+1920, + .vso_end = 2164+1920, + .vso_bline = 51, + .vso_eline = 53, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 2249, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_2160p30 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x8, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1000, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 140+3840, + .max_pxcnt = 3840+560-1, + .hspuls_begin = 2156+1920, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059+1920, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 3987, + .vavon_bline = 89, + .vavon_eline = 2248, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156+1920, + .vso_begin = 2100+1920, + .vso_end = 2164+1920, + .vso_bline = 51, + .vso_eline = 53, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 2249, + }, +}; + struct meson_hdmi_venc_vic_mode { unsigned int vic; union meson_hdmi_venc_mode *mode; @@ -717,6 +843,9 @@ struct meson_hdmi_venc_vic_mode { { 34, &meson_hdmi_encp_mode_1080p30 }, { 31, &meson_hdmi_encp_mode_1080p50 }, { 16, &meson_hdmi_encp_mode_1080p60 }, + { 93, &meson_hdmi_encp_mode_2160p24 }, + { 94, &meson_hdmi_encp_mode_2160p25 }, + { 95, &meson_hdmi_encp_mode_2160p30 }, { 0, NULL}, /* sentinel */ }; diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c index 6bcfa527c180..0ba87ff95530 100644 --- a/drivers/gpu/drm/meson/meson_viu.c +++ b/drivers/gpu/drm/meson/meson_viu.c @@ -296,6 +296,33 @@ static void meson_viu_load_matrix(struct meson_drm *priv) true); } +/* VIU OSD1 Reset as workaround for GXL+ Alpha OSD Bug */ +void meson_viu_osd1_reset(struct meson_drm *priv) +{ + uint32_t osd1_fifo_ctrl_stat, osd1_ctrl_stat2; + + /* Save these 2 registers state */ + osd1_fifo_ctrl_stat = readl_relaxed( + priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT)); + osd1_ctrl_stat2 = readl_relaxed( + priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); + + /* Reset OSD1 */ + writel_bits_relaxed(BIT(0), BIT(0), + priv->io_base + _REG(VIU_SW_RESET)); + writel_bits_relaxed(BIT(0), 0, + priv->io_base + _REG(VIU_SW_RESET)); + + /* Rewrite these registers state lost in the reset */ + writel_relaxed(osd1_fifo_ctrl_stat, + priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT)); + writel_relaxed(osd1_ctrl_stat2, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); + + /* Reload the conversion matrix */ + meson_viu_load_matrix(priv); +} + void meson_viu_init(struct meson_drm *priv) { uint32_t reg; @@ -329,6 +356,21 @@ void meson_viu_init(struct meson_drm *priv) 0xff << OSD_REPLACE_SHIFT, priv->io_base + _REG(VIU_OSD2_CTRL_STAT2)); + /* Disable VD1 AFBC */ + /* di_mif0_en=0 mif0_to_vpp_en=0 di_mad_en=0 */ + writel_bits_relaxed(0x7 << 16, 0, + priv->io_base + _REG(VIU_MISC_CTRL0)); + /* afbc vd1 set=0 */ + writel_bits_relaxed(BIT(20), 0, + priv->io_base + _REG(VIU_MISC_CTRL0)); + writel_relaxed(0, priv->io_base + _REG(AFBC_ENABLE)); + + writel_relaxed(0x00FF00C0, + priv->io_base + _REG(VD1_IF0_LUMA_FIFO_SIZE)); + writel_relaxed(0x00FF00C0, + priv->io_base + _REG(VD2_IF0_LUMA_FIFO_SIZE)); + + priv->viu.osd1_enabled = false; priv->viu.osd1_commit = false; priv->viu.osd1_interlace = false; diff --git a/drivers/gpu/drm/meson/meson_viu.h b/drivers/gpu/drm/meson/meson_viu.h index 073b1910bd1b..0f84bddd2ff0 100644 --- a/drivers/gpu/drm/meson/meson_viu.h +++ b/drivers/gpu/drm/meson/meson_viu.h @@ -59,6 +59,7 @@ #define OSD_REPLACE_EN BIT(14) #define OSD_REPLACE_SHIFT 6 +void meson_viu_osd1_reset(struct meson_drm *priv); void meson_viu_init(struct meson_drm *priv); #endif /* __MESON_VIU_H */ diff --git a/drivers/gpu/drm/meson/meson_vpp.c b/drivers/gpu/drm/meson/meson_vpp.c index 27356f81a0ab..f9efb431e953 100644 --- a/drivers/gpu/drm/meson/meson_vpp.c +++ b/drivers/gpu/drm/meson/meson_vpp.c @@ -51,52 +51,6 @@ void meson_vpp_setup_mux(struct meson_drm *priv, unsigned int mux) writel(mux, priv->io_base + _REG(VPU_VIU_VENC_MUX_CTRL)); } -/* - * When the output is interlaced, the OSD must switch between - * each field using the INTERLACE_SEL_ODD (0) of VIU_OSD1_BLK0_CFG_W0 - * at each vsync. - * But the vertical scaler can provide such funtionnality if - * is configured for 2:1 scaling with interlace options enabled. - */ -void meson_vpp_setup_interlace_vscaler_osd1(struct meson_drm *priv, - struct drm_rect *input) -{ - writel_relaxed(BIT(3) /* Enable scaler */ | - BIT(2), /* Select OSD1 */ - priv->io_base + _REG(VPP_OSD_SC_CTRL0)); - - writel_relaxed(((drm_rect_width(input) - 1) << 16) | - (drm_rect_height(input) - 1), - priv->io_base + _REG(VPP_OSD_SCI_WH_M1)); - /* 2:1 scaling */ - writel_relaxed(((input->x1) << 16) | (input->x2), - priv->io_base + _REG(VPP_OSD_SCO_H_START_END)); - writel_relaxed(((input->y1 >> 1) << 16) | (input->y2 >> 1), - priv->io_base + _REG(VPP_OSD_SCO_V_START_END)); - - /* 2:1 scaling values */ - writel_relaxed(BIT(16), priv->io_base + _REG(VPP_OSD_VSC_INI_PHASE)); - writel_relaxed(BIT(25), priv->io_base + _REG(VPP_OSD_VSC_PHASE_STEP)); - - writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); - - writel_relaxed((4 << 0) /* osd_vsc_bank_length */ | - (4 << 3) /* osd_vsc_top_ini_rcv_num0 */ | - (1 << 8) /* osd_vsc_top_rpt_p0_num0 */ | - (6 << 11) /* osd_vsc_bot_ini_rcv_num0 */ | - (2 << 16) /* osd_vsc_bot_rpt_p0_num0 */ | - BIT(23) /* osd_prog_interlace */ | - BIT(24), /* Enable vertical scaler */ - priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); -} - -void meson_vpp_disable_interlace_vscaler_osd1(struct meson_drm *priv) -{ - writel_relaxed(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0)); - writel_relaxed(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); - writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); -} - static unsigned int vpp_filter_coefs_4point_bspline[] = { 0x15561500, 0x14561600, 0x13561700, 0x12561800, 0x11551a00, 0x11541b00, 0x10541c00, 0x0f541d00, @@ -122,6 +76,31 @@ static void meson_vpp_write_scaling_filter_coefs(struct meson_drm *priv, priv->io_base + _REG(VPP_OSD_SCALE_COEF)); } +static const uint32_t vpp_filter_coefs_bicubic[] = { + 0x00800000, 0x007f0100, 0xff7f0200, 0xfe7f0300, + 0xfd7e0500, 0xfc7e0600, 0xfb7d0800, 0xfb7c0900, + 0xfa7b0b00, 0xfa7a0dff, 0xf9790fff, 0xf97711ff, + 0xf87613ff, 0xf87416fe, 0xf87218fe, 0xf8701afe, + 0xf76f1dfd, 0xf76d1ffd, 0xf76b21fd, 0xf76824fd, + 0xf76627fc, 0xf76429fc, 0xf7612cfc, 0xf75f2ffb, + 0xf75d31fb, 0xf75a34fb, 0xf75837fa, 0xf7553afa, + 0xf8523cfa, 0xf8503ff9, 0xf84d42f9, 0xf84a45f9, + 0xf84848f8 +}; + +static void meson_vpp_write_vd_scaling_filter_coefs(struct meson_drm *priv, + const unsigned int *coefs, + bool is_horizontal) +{ + int i; + + writel_relaxed(is_horizontal ? BIT(8) : 0, + priv->io_base + _REG(VPP_SCALE_COEF_IDX)); + for (i = 0; i < 33; i++) + writel_relaxed(coefs[i], + priv->io_base + _REG(VPP_SCALE_COEF)); +} + void meson_vpp_init(struct meson_drm *priv) { /* set dummy data default YUV black */ @@ -150,17 +129,34 @@ void meson_vpp_init(struct meson_drm *priv) /* Force all planes off */ writel_bits_relaxed(VPP_OSD1_POSTBLEND | VPP_OSD2_POSTBLEND | - VPP_VD1_POSTBLEND | VPP_VD2_POSTBLEND, 0, + VPP_VD1_POSTBLEND | VPP_VD2_POSTBLEND | + VPP_VD1_PREBLEND | VPP_VD2_PREBLEND, 0, priv->io_base + _REG(VPP_MISC)); + /* Setup default VD settings */ + writel_relaxed(4096, + priv->io_base + _REG(VPP_PREBLEND_VD1_H_START_END)); + writel_relaxed(4096, + priv->io_base + _REG(VPP_BLEND_VD2_H_START_END)); + /* Disable Scalers */ writel_relaxed(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0)); writel_relaxed(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); + writel_relaxed(4 | (4 << 8) | BIT(15), + priv->io_base + _REG(VPP_SC_MISC)); + + writel_relaxed(1, priv->io_base + _REG(VPP_VADJ_CTRL)); /* Write in the proper filter coefficients. */ meson_vpp_write_scaling_filter_coefs(priv, vpp_filter_coefs_4point_bspline, false); meson_vpp_write_scaling_filter_coefs(priv, vpp_filter_coefs_4point_bspline, true); + + /* Write the VD proper filter coefficients. */ + meson_vpp_write_vd_scaling_filter_coefs(priv, vpp_filter_coefs_bicubic, + false); + meson_vpp_write_vd_scaling_filter_coefs(priv, vpp_filter_coefs_bicubic, + true); } diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c index bddd625ab91b..f71d8cf2261b 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c @@ -144,7 +144,7 @@ static int mdp5_global_obj_init(struct mdp5_kms *mdp5_kms) state->mdp5_kms = mdp5_kms; - drm_atomic_private_obj_init(&mdp5_kms->glob_state, + drm_atomic_private_obj_init(mdp5_kms->dev, &mdp5_kms->glob_state, &state->base, &mdp5_global_state_funcs); return 0; diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 2393e6d16ffd..88ba003979e6 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -417,7 +417,7 @@ static int mxsfb_probe(struct platform_device *pdev) err_unload: mxsfb_unload(drm); err_free: - drm_dev_unref(drm); + drm_dev_put(drm); return ret; } @@ -428,7 +428,7 @@ static int mxsfb_remove(struct platform_device *pdev) drm_dev_unregister(drm); mxsfb_unload(drm); - drm_dev_unref(drm); + drm_dev_put(drm); return 0; } diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 6cbbae3f438b..4a56841958c8 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1056,13 +1056,6 @@ nv50_mstm_prepare(struct nv50_mstm *mstm) } static void -nv50_mstm_hotplug(struct drm_dp_mst_topology_mgr *mgr) -{ - struct nv50_mstm *mstm = nv50_mstm(mgr); - drm_kms_helper_hotplug_event(mstm->outp->base.base.dev); -} - -static void nv50_mstm_destroy_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_connector *connector) { @@ -1113,7 +1106,6 @@ nv50_mstm = { .add_connector = nv50_mstm_add_connector, .register_connector = nv50_mstm_register_connector, .destroy_connector = nv50_mstm_destroy_connector, - .hotplug = nv50_mstm_hotplug, }; void diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c index fa8bfa7c492d..33c22ee036f8 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c @@ -96,7 +96,7 @@ static int s6d16d0_prepare(struct drm_panel *panel) ret = mipi_dsi_dcs_set_tear_on(dsi, MIPI_DSI_DCS_TEAR_MODE_VBLANK); if (ret) { - DRM_DEV_ERROR(s6->dev, "failed to enble vblank TE (%d)\n", + DRM_DEV_ERROR(s6->dev, "failed to enable vblank TE (%d)\n", ret); return ret; } diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 5fbee837b0db..9c69e739a524 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -618,6 +618,30 @@ static const struct panel_desc auo_g070vvn01 = { }, }; +static const struct drm_display_mode auo_g101evn010_mode = { + .clock = 68930, + .hdisplay = 1280, + .hsync_start = 1280 + 82, + .hsync_end = 1280 + 82 + 2, + .htotal = 1280 + 82 + 2 + 84, + .vdisplay = 800, + .vsync_start = 800 + 8, + .vsync_end = 800 + 8 + 2, + .vtotal = 800 + 8 + 2 + 6, + .vrefresh = 60, +}; + +static const struct panel_desc auo_g101evn010 = { + .modes = &auo_g101evn010_mode, + .num_modes = 1, + .bpc = 6, + .size = { + .width = 216, + .height = 135, + }, + .bus_format = MEDIA_BUS_FMT_RGB666_1X18, +}; + static const struct drm_display_mode auo_g104sn02_mode = { .clock = 40000, .hdisplay = 800, @@ -2494,6 +2518,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "auo,g070vvn01", .data = &auo_g070vvn01, }, { + .compatible = "auo,g101evn010", + .data = &auo_g101evn010, + }, { .compatible = "auo,g104sn02", .data = &auo_g104sn02, }, { diff --git a/drivers/gpu/drm/pl111/pl111_vexpress.c b/drivers/gpu/drm/pl111/pl111_vexpress.c index 5fa0441bb6df..38c938c9adda 100644 --- a/drivers/gpu/drm/pl111/pl111_vexpress.c +++ b/drivers/gpu/drm/pl111/pl111_vexpress.c @@ -55,6 +55,8 @@ int pl111_vexpress_clcd_init(struct device *dev, } } + of_node_put(root); + /* * If there is a coretile HDLCD and it has a driver, * do not mux the CLCD on the motherboard to the DVI. diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index 2ce9a8dcec84..72a1784dae54 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -622,10 +622,14 @@ static void qxl_cursor_atomic_update(struct drm_plane *plane, if (ret) goto out_kunmap; - ret = qxl_release_reserve_list(release, true); + ret = qxl_bo_pin(cursor_bo); if (ret) goto out_free_bo; + ret = qxl_release_reserve_list(release, true); + if (ret) + goto out_unpin; + ret = qxl_bo_kmap(cursor_bo, (void **)&cursor); if (ret) goto out_backoff; @@ -670,15 +674,17 @@ static void qxl_cursor_atomic_update(struct drm_plane *plane, qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false); qxl_release_fence_buffer_objects(release); - if (old_cursor_bo) - qxl_bo_unref(&old_cursor_bo); - + if (old_cursor_bo != NULL) + qxl_bo_unpin(old_cursor_bo); + qxl_bo_unref(&old_cursor_bo); qxl_bo_unref(&cursor_bo); return; out_backoff: qxl_release_backoff_reserve_list(release); +out_unpin: + qxl_bo_unpin(cursor_bo); out_free_bo: qxl_bo_unref(&cursor_bo); out_kunmap: @@ -757,7 +763,7 @@ static int qxl_plane_prepare_fb(struct drm_plane *plane, } } - ret = qxl_bo_pin(user_bo, QXL_GEM_DOMAIN_CPU, NULL); + ret = qxl_bo_pin(user_bo); if (ret) return ret; @@ -1004,7 +1010,6 @@ static void qxl_conn_destroy(struct drm_connector *connector) } static const struct drm_connector_funcs qxl_connector_funcs = { - .dpms = drm_helper_connector_dpms, .detect = qxl_conn_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = qxl_conn_destroy, @@ -1104,7 +1109,7 @@ int qxl_create_monitors_object(struct qxl_device *qdev) } qdev->monitors_config_bo = gem_to_qxl_bo(gobj); - ret = qxl_bo_pin(qdev->monitors_config_bo, QXL_GEM_DOMAIN_VRAM, NULL); + ret = qxl_bo_pin(qdev->monitors_config_bo); if (ret) return ret; diff --git a/drivers/gpu/drm/qxl/qxl_draw.c b/drivers/gpu/drm/qxl/qxl_draw.c index c34e45662965..c408bb83c7a9 100644 --- a/drivers/gpu/drm/qxl/qxl_draw.c +++ b/drivers/gpu/drm/qxl/qxl_draw.c @@ -247,8 +247,7 @@ void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image, qxl_release_fence_buffer_objects(release); out_free_palette: - if (palette_bo) - qxl_bo_unref(&palette_bo); + qxl_bo_unref(&palette_bo); out_free_image: qxl_image_free_objects(qdev, dimage); out_free_drawable: diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c index 7e047c985ea6..a819d24225d2 100644 --- a/drivers/gpu/drm/qxl/qxl_fb.c +++ b/drivers/gpu/drm/qxl/qxl_fb.c @@ -111,7 +111,7 @@ static int qxlfb_create_pinned_object(struct qxl_device *qdev, qbo->surf.stride = mode_cmd->pitches[0]; qbo->surf.format = SPICE_SURFACE_FMT_32_xRGB; - ret = qxl_bo_pin(qbo, QXL_GEM_DOMAIN_SURFACE, NULL); + ret = qxl_bo_pin(qbo); if (ret) { goto out_unref; } diff --git a/drivers/gpu/drm/qxl/qxl_kms.c b/drivers/gpu/drm/qxl/qxl_kms.c index f6975d7c7d10..15238a413f9d 100644 --- a/drivers/gpu/drm/qxl/qxl_kms.c +++ b/drivers/gpu/drm/qxl/qxl_kms.c @@ -313,10 +313,8 @@ error: void qxl_device_fini(struct qxl_device *qdev) { - if (qdev->current_release_bo[0]) - qxl_bo_unref(&qdev->current_release_bo[0]); - if (qdev->current_release_bo[1]) - qxl_bo_unref(&qdev->current_release_bo[1]); + qxl_bo_unref(&qdev->current_release_bo[0]); + qxl_bo_unref(&qdev->current_release_bo[1]); flush_work(&qdev->gc_work); qxl_ring_free(qdev->command_ring); qxl_ring_free(qdev->cursor_ring); diff --git a/drivers/gpu/drm/qxl/qxl_object.c b/drivers/gpu/drm/qxl/qxl_object.c index f67a3c535afb..91f3bbc73ecc 100644 --- a/drivers/gpu/drm/qxl/qxl_object.c +++ b/drivers/gpu/drm/qxl/qxl_object.c @@ -186,13 +186,9 @@ void qxl_bo_kunmap_atomic_page(struct qxl_device *qdev, struct qxl_bo *bo, void *pmap) { struct ttm_mem_type_manager *man = &bo->tbo.bdev->man[bo->tbo.mem.mem_type]; - struct io_mapping *map; - if (bo->tbo.mem.mem_type == TTM_PL_VRAM) - map = qdev->vram_mapping; - else if (bo->tbo.mem.mem_type == TTM_PL_PRIV) - map = qdev->surface_mapping; - else + if ((bo->tbo.mem.mem_type != TTM_PL_VRAM) && + (bo->tbo.mem.mem_type != TTM_PL_PRIV)) goto fallback; io_mapping_unmap_atomic(pmap); @@ -200,7 +196,7 @@ void qxl_bo_kunmap_atomic_page(struct qxl_device *qdev, (void) ttm_mem_io_lock(man, false); ttm_mem_io_free(bo->tbo.bdev, &bo->tbo.mem); ttm_mem_io_unlock(man); - return ; + return; fallback: qxl_bo_kunmap(bo); } @@ -220,7 +216,7 @@ struct qxl_bo *qxl_bo_ref(struct qxl_bo *bo) return bo; } -static int __qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr) +static int __qxl_bo_pin(struct qxl_bo *bo) { struct ttm_operation_ctx ctx = { false, false }; struct drm_device *ddev = bo->gem_base.dev; @@ -228,16 +224,12 @@ static int __qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr) if (bo->pin_count) { bo->pin_count++; - if (gpu_addr) - *gpu_addr = qxl_bo_gpu_offset(bo); return 0; } - qxl_ttm_placement_from_domain(bo, domain, true); + qxl_ttm_placement_from_domain(bo, bo->type, true); r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); if (likely(r == 0)) { bo->pin_count = 1; - if (gpu_addr != NULL) - *gpu_addr = qxl_bo_gpu_offset(bo); } if (unlikely(r != 0)) dev_err(ddev->dev, "%p pin failed\n", bo); @@ -270,7 +262,7 @@ static int __qxl_bo_unpin(struct qxl_bo *bo) * beforehand, use the internal version directly __qxl_bo_pin. * */ -int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr) +int qxl_bo_pin(struct qxl_bo *bo) { int r; @@ -278,7 +270,7 @@ int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr) if (r) return r; - r = __qxl_bo_pin(bo, bo->type, NULL); + r = __qxl_bo_pin(bo); qxl_bo_unreserve(bo); return r; } diff --git a/drivers/gpu/drm/qxl/qxl_object.h b/drivers/gpu/drm/qxl/qxl_object.h index b40fc9a10406..255b914e2a7b 100644 --- a/drivers/gpu/drm/qxl/qxl_object.h +++ b/drivers/gpu/drm/qxl/qxl_object.h @@ -97,7 +97,7 @@ void *qxl_bo_kmap_atomic_page(struct qxl_device *qdev, struct qxl_bo *bo, int pa void qxl_bo_kunmap_atomic_page(struct qxl_device *qdev, struct qxl_bo *bo, void *map); extern struct qxl_bo *qxl_bo_ref(struct qxl_bo *bo); extern void qxl_bo_unref(struct qxl_bo **bo); -extern int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr); +extern int qxl_bo_pin(struct qxl_bo *bo); extern int qxl_bo_unpin(struct qxl_bo *bo); extern void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned); extern bool qxl_ttm_bo_is_qxl_bo(struct ttm_buffer_object *bo); diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c index 3813ec198900..0a693fede05e 100644 --- a/drivers/gpu/drm/qxl/qxl_release.c +++ b/drivers/gpu/drm/qxl/qxl_release.c @@ -427,8 +427,6 @@ void qxl_release_fence_buffer_objects(struct qxl_release *release) struct ttm_buffer_object *bo; struct ttm_bo_global *glob; struct ttm_bo_device *bdev; - struct ttm_bo_driver *driver; - struct qxl_bo *qbo; struct ttm_validate_buffer *entry; struct qxl_device *qdev; @@ -449,14 +447,12 @@ void qxl_release_fence_buffer_objects(struct qxl_release *release) release->id | 0xf0000000, release->base.seqno); trace_dma_fence_emit(&release->base); - driver = bdev->driver; glob = bdev->glob; spin_lock(&glob->lru_lock); list_for_each_entry(entry, &release->bos, head) { bo = entry->bo; - qbo = to_qxl_bo(bo); reservation_object_add_shared_fence(bo->resv, &release->base); ttm_bo_add_to_lru(bo); diff --git a/drivers/gpu/drm/r128/r128_cce.c b/drivers/gpu/drm/r128/r128_cce.c index c9890afe69d6..b91af1bf531b 100644 --- a/drivers/gpu/drm/r128/r128_cce.c +++ b/drivers/gpu/drm/r128/r128_cce.c @@ -560,11 +560,12 @@ static int r128_do_init_cce(struct drm_device *dev, drm_r128_init_t *init) dev_priv->gart_info.addr = NULL; dev_priv->gart_info.bus_addr = 0; dev_priv->gart_info.gart_reg_if = DRM_ATI_GART_PCI; - if (!drm_ati_pcigart_init(dev, &dev_priv->gart_info)) { + rc = drm_ati_pcigart_init(dev, &dev_priv->gart_info); + if (rc) { DRM_ERROR("failed to init PCI GART!\n"); dev->dev_private = (void *)dev_priv; r128_do_cleanup_cce(dev); - return -ENOMEM; + return rc; } R128_WRITE(R128_PCI_GART_PAGE, dev_priv->gart_info.bus_addr); #if IS_ENABLED(CONFIG_AGP) diff --git a/drivers/gpu/drm/radeon/radeon_dp_mst.c b/drivers/gpu/drm/radeon/radeon_dp_mst.c index 84b3ad2172a3..a0c70e27ab65 100644 --- a/drivers/gpu/drm/radeon/radeon_dp_mst.c +++ b/drivers/gpu/drm/radeon/radeon_dp_mst.c @@ -320,19 +320,10 @@ static void radeon_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr, DRM_DEBUG_KMS("\n"); } -static void radeon_dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr) -{ - struct radeon_connector *master = container_of(mgr, struct radeon_connector, mst_mgr); - struct drm_device *dev = master->base.dev; - - drm_kms_helper_hotplug_event(dev); -} - static const struct drm_dp_mst_topology_cbs mst_cbs = { .add_connector = radeon_dp_add_mst_connector, .register_connector = radeon_dp_register_mst_connector, .destroy_connector = radeon_dp_destroy_mst_connector, - .hotplug = radeon_dp_mst_hotplug, }; static struct diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 17741843cf51..90dacab67be5 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -226,9 +226,6 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) * system clock, and have no internal clock divider. */ - if (WARN_ON(!rcrtc->extclock)) - return; - /* * The H3 ES1.x exhibits dot clock duty cycle stability issues. * We can work around them by configuring the DPLL to twice the @@ -701,7 +698,7 @@ static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc, * CRTC will be put later in .atomic_disable(). * * If a mode set is not in progress the CRTC is enabled, and the - * following get call will be a no-op. There is thus no need to belance + * following get call will be a no-op. There is thus no need to balance * it in .atomic_flush() either. */ rcar_du_crtc_get(rcrtc); @@ -738,10 +735,22 @@ enum drm_mode_status rcar_du_crtc_mode_valid(struct drm_crtc *crtc, struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); struct rcar_du_device *rcdu = rcrtc->group->dev; bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; + unsigned int vbp; if (interlaced && !rcar_du_has(rcdu, RCAR_DU_FEATURE_INTERLACED)) return MODE_NO_INTERLACE; + /* + * The hardware requires a minimum combined horizontal sync and back + * porch of 20 pixels and a minimum vertical back porch of 3 lines. + */ + if (mode->htotal - mode->hsync_start < 20) + return MODE_HBLANK_NARROW; + + vbp = (mode->vtotal - mode->vsync_end) / (interlaced ? 2 : 1); + if (vbp < 3) + return MODE_VBLANK_NARROW; + return MODE_OK; } @@ -1002,7 +1011,7 @@ unlock: drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); - return 0; + return ret; } static const struct drm_crtc_funcs crtc_funcs_gen2 = { @@ -1113,9 +1122,16 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex, clk = devm_clk_get(rcdu->dev, clk_name); if (!IS_ERR(clk)) { rcrtc->extclock = clk; - } else if (PTR_ERR(rcrtc->clock) == -EPROBE_DEFER) { - dev_info(rcdu->dev, "can't get external clock %u\n", hwindex); + } else if (PTR_ERR(clk) == -EPROBE_DEFER) { return -EPROBE_DEFER; + } else if (rcdu->info->dpll_mask & BIT(hwindex)) { + /* + * DU channels that have a display PLL can't use the internal + * system clock and thus require an external clock. + */ + ret = PTR_ERR(clk); + dev_err(rcdu->dev, "can't get dclkin.%u: %d\n", hwindex, ret); + return ret; } init_waitqueue_head(&rcrtc->flip_wait); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 7015974c247a..f50a3b1864bb 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -21,6 +21,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> +#include <drm/drm_fb_helper.h> #include <drm/drm_gem_cma_helper.h> #include "rcar_du_drv.h" @@ -41,7 +42,7 @@ static const struct rcar_du_device_info rzg1_du_r8a7743_info = { .channels_mask = BIT(1) | BIT(0), .routes = { /* - * R8A7743 has one RGB output and one LVDS output + * R8A774[34] has one RGB output and one LVDS output */ [RCAR_DU_OUTPUT_DPAD0] = { .possible_crtcs = BIT(1) | BIT(0), @@ -77,6 +78,33 @@ static const struct rcar_du_device_info rzg1_du_r8a7745_info = { }, }; +static const struct rcar_du_device_info rzg1_du_r8a77470_info = { + .gen = 2, + .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK + | RCAR_DU_FEATURE_EXT_CTRL_REGS + | RCAR_DU_FEATURE_INTERLACED + | RCAR_DU_FEATURE_TVM_SYNC, + .channels_mask = BIT(1) | BIT(0), + .routes = { + /* + * R8A77470 has two RGB outputs, one LVDS output, and + * one (currently unsupported) analog video output + */ + [RCAR_DU_OUTPUT_DPAD0] = { + .possible_crtcs = BIT(0), + .port = 0, + }, + [RCAR_DU_OUTPUT_DPAD1] = { + .possible_crtcs = BIT(1), + .port = 1, + }, + [RCAR_DU_OUTPUT_LVDS0] = { + .possible_crtcs = BIT(0) | BIT(1), + .port = 2, + }, + }, +}; + static const struct rcar_du_device_info rcar_du_r8a7779_info = { .gen = 2, .features = RCAR_DU_FEATURE_INTERLACED @@ -341,7 +369,9 @@ static const struct rcar_du_device_info rcar_du_r8a7799x_info = { static const struct of_device_id rcar_du_of_table[] = { { .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info }, + { .compatible = "renesas,du-r8a7744", .data = &rzg1_du_r8a7743_info }, { .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info }, + { .compatible = "renesas,du-r8a77470", .data = &rzg1_du_r8a77470_info }, { .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info }, { .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info }, { .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info }, @@ -363,19 +393,11 @@ MODULE_DEVICE_TABLE(of, rcar_du_of_table); * DRM operations */ -static void rcar_du_lastclose(struct drm_device *dev) -{ - struct rcar_du_device *rcdu = dev->dev_private; - - drm_fbdev_cma_restore_mode(rcdu->fbdev); -} - DEFINE_DRM_GEM_CMA_FOPS(rcar_du_fops); static struct drm_driver rcar_du_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, - .lastclose = rcar_du_lastclose, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, @@ -431,13 +453,10 @@ static int rcar_du_remove(struct platform_device *pdev) drm_dev_unregister(ddev); - if (rcdu->fbdev) - drm_fbdev_cma_fini(rcdu->fbdev); - drm_kms_helper_poll_fini(ddev); drm_mode_config_cleanup(ddev); - drm_dev_unref(ddev); + drm_dev_put(ddev); return 0; } @@ -493,6 +512,8 @@ static int rcar_du_probe(struct platform_device *pdev) DRM_INFO("Device %s probed\n", dev_name(&pdev->dev)); + drm_fbdev_generic_setup(ddev, 32); + return 0; error: diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 9f5563296c5a..a68da79b424e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -20,7 +20,6 @@ struct clk; struct device; struct drm_device; -struct drm_fbdev_cma; struct rcar_du_device; #define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK BIT(0) /* Per-CRTC IRQ and clock */ @@ -78,7 +77,6 @@ struct rcar_du_device { void __iomem *mmio; struct drm_device *ddev; - struct drm_fbdev_cma *fbdev; struct rcar_du_crtc crtcs[RCAR_DU_MAX_CRTCS]; unsigned int num_crtcs; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 4ebd61ecbee1..9c7007d45408 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -255,13 +255,6 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, return drm_gem_fb_create(dev, file_priv, mode_cmd); } -static void rcar_du_output_poll_changed(struct drm_device *dev) -{ - struct rcar_du_device *rcdu = dev->dev_private; - - drm_fbdev_cma_hotplug_event(rcdu->fbdev); -} - /* ----------------------------------------------------------------------------- * Atomic Check and Update */ @@ -308,7 +301,6 @@ static const struct drm_mode_config_helper_funcs rcar_du_mode_config_helper = { static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = { .fb_create = rcar_du_fb_create, - .output_poll_changed = rcar_du_output_poll_changed, .atomic_check = rcar_du_atomic_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -543,7 +535,6 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) struct drm_device *dev = rcdu->ddev; struct drm_encoder *encoder; - struct drm_fbdev_cma *fbdev; unsigned int dpad0_sources; unsigned int num_encoders; unsigned int num_groups; @@ -582,7 +573,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) * Initialize vertical blanking interrupts handling. Start with vblank * disabled for all CRTCs. */ - ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1); + ret = drm_vblank_init(dev, rcdu->num_crtcs); if (ret < 0) return ret; @@ -682,17 +673,5 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) drm_kms_helper_poll_init(dev); - if (dev->mode_config.num_connector) { - fbdev = drm_fbdev_cma_init(dev, 32, - dev->mode_config.num_connector); - if (IS_ERR(fbdev)) - return PTR_ERR(fbdev); - - rcdu->fbdev = fbdev; - } else { - dev_info(rcdu->dev, - "no connector found, disabling fbdev emulation\n"); - } - return 0; } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 9e07758a755c..39d5ae3fdf72 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -783,13 +783,14 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp) drm_plane_helper_add(&plane->plane, &rcar_du_plane_helper_funcs); + drm_plane_create_alpha_property(&plane->plane); + if (type == DRM_PLANE_TYPE_PRIMARY) continue; drm_object_attach_property(&plane->plane.base, rcdu->props.colorkey, RCAR_DU_COLORKEY_NONE); - drm_plane_create_alpha_property(&plane->plane); drm_plane_create_zpos_property(&plane->plane, 1, 1, 7); } diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index 173d7ad0b991..534a128a869d 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -790,6 +790,7 @@ static const struct of_device_id rcar_lvds_of_table[] = { { .compatible = "renesas,r8a7793-lvds", .data = &rcar_lvds_gen2_info }, { .compatible = "renesas,r8a7795-lvds", .data = &rcar_lvds_gen3_info }, { .compatible = "renesas,r8a7796-lvds", .data = &rcar_lvds_gen3_info }, + { .compatible = "renesas,r8a77965-lvds", .data = &rcar_lvds_gen3_info }, { .compatible = "renesas,r8a77970-lvds", .data = &rcar_lvds_r8a77970_info }, { .compatible = "renesas,r8a77980-lvds", .data = &rcar_lvds_gen3_info }, { .compatible = "renesas,r8a77990-lvds", .data = &rcar_lvds_r8a77990_info }, diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index 3105965fc260..5a485489a1e2 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -147,7 +147,7 @@ static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp, } static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp, - u8 *buff, u8 buff_size) + u8 *buff, u16 buff_size) { u32 i; int ret; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index fb70fb486fbf..db8358e6d230 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -18,6 +18,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_flip_work.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_plane_helper.h> #ifdef CONFIG_DRM_ANALOGIX_DP #include <drm/bridge/analogix_dp.h> @@ -823,6 +824,7 @@ static const struct drm_plane_helper_funcs plane_helper_funcs = { .atomic_check = vop_plane_atomic_check, .atomic_update = vop_plane_atomic_update, .atomic_disable = vop_plane_atomic_disable, + .prepare_fb = drm_gem_fb_prepare_fb, }; static const struct drm_plane_funcs vop_plane_funcs = { diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index 6ececad6f845..8554102a6ead 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -194,7 +194,7 @@ static int shmob_drm_remove(struct platform_device *pdev) drm_kms_helper_poll_fini(ddev); drm_mode_config_cleanup(ddev); drm_irq_uninstall(ddev); - drm_dev_unref(ddev); + drm_dev_put(ddev); return 0; } @@ -290,7 +290,7 @@ err_modeset_cleanup: drm_kms_helper_poll_fini(ddev); drm_mode_config_cleanup(ddev); err_free_drm_dev: - drm_dev_unref(ddev); + drm_dev_put(ddev); return ret; } diff --git a/drivers/gpu/drm/sti/sti_crtc.c b/drivers/gpu/drm/sti/sti_crtc.c index 61c2379fba87..ed76e52eb213 100644 --- a/drivers/gpu/drm/sti/sti_crtc.c +++ b/drivers/gpu/drm/sti/sti_crtc.c @@ -252,10 +252,8 @@ int sti_crtc_vblank_cb(struct notifier_block *nb, struct sti_compositor *compo; struct drm_crtc *crtc = data; struct sti_mixer *mixer; - struct sti_private *priv; unsigned int pipe; - priv = crtc->dev->dev_private; pipe = drm_crtc_index(crtc); compo = container_of(nb, struct sti_compositor, vtg_vblank_nb[pipe]); mixer = compo->mixer[pipe]; diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index bf49c55b0f2c..9e9255ee59cd 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c @@ -48,8 +48,12 @@ static const u32 sunxi_rgb2yuv_coef[12] = { /* * These coefficients are taken from the A33 BSP from Allwinner. * - * The formula is for each component, each coefficient being multiplied by - * 1024 and each constant being multiplied by 16: + * The first three values of each row are coded as 13-bit signed fixed-point + * numbers, with 10 bits for the fractional part. The fourth value is a + * constant coded as a 14-bit signed fixed-point number with 4 bits for the + * fractional part. + * + * The values in table order give the following colorspace translation: * G = 1.164 * Y - 0.391 * U - 0.813 * V + 135 * R = 1.164 * Y + 1.596 * V - 222 * B = 1.164 * Y + 2.018 * U + 276 @@ -155,6 +159,36 @@ static int sun4i_backend_drm_format_to_layer(u32 format, u32 *mode) return 0; } +static const uint32_t sun4i_backend_formats[] = { + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ARGB4444, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGBA4444, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, +}; + +bool sun4i_backend_format_is_supported(uint32_t fmt, uint64_t modifier) +{ + unsigned int i; + + if (modifier != DRM_FORMAT_MOD_LINEAR) + return false; + + for (i = 0; i < ARRAY_SIZE(sun4i_backend_formats); i++) + if (sun4i_backend_formats[i] == fmt) + return true; + + return false; +} + int sun4i_backend_update_layer_coord(struct sun4i_backend *backend, int layer, struct drm_plane *plane) { @@ -395,6 +429,15 @@ int sun4i_backend_update_layer_zpos(struct sun4i_backend *backend, int layer, return 0; } +void sun4i_backend_cleanup_layer(struct sun4i_backend *backend, + int layer) +{ + regmap_update_bits(backend->engine.regs, + SUN4I_BACKEND_ATTCTL_REG0(layer), + SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN | + SUN4I_BACKEND_ATTCTL_REG0_LAY_YUVEN, 0); +} + static bool sun4i_backend_plane_uses_scaler(struct drm_plane_state *state) { u16 src_h = state->src_h >> 16; @@ -413,11 +456,50 @@ static bool sun4i_backend_plane_uses_frontend(struct drm_plane_state *state) { struct sun4i_layer *layer = plane_to_sun4i_layer(state->plane); struct sun4i_backend *backend = layer->backend; + uint32_t format = state->fb->format->format; + uint64_t modifier = state->fb->modifier; if (IS_ERR(backend->frontend)) return false; - return sun4i_backend_plane_uses_scaler(state); + if (!sun4i_frontend_format_is_supported(format, modifier)) + return false; + + if (!sun4i_backend_format_is_supported(format, modifier)) + return true; + + /* + * TODO: The backend alone allows 2x and 4x integer scaling, including + * support for an alpha component (which the frontend doesn't support). + * Use the backend directly instead of the frontend in this case, with + * another test to return false. + */ + + if (sun4i_backend_plane_uses_scaler(state)) + return true; + + /* + * Here the format is supported by both the frontend and the backend + * and no frontend scaling is required, so use the backend directly. + */ + return false; +} + +static bool sun4i_backend_plane_is_supported(struct drm_plane_state *state, + bool *uses_frontend) +{ + if (sun4i_backend_plane_uses_frontend(state)) { + *uses_frontend = true; + return true; + } + + *uses_frontend = false; + + /* Scaling is not supported without the frontend. */ + if (sun4i_backend_plane_uses_scaler(state)) + return false; + + return true; } static void sun4i_backend_atomic_begin(struct sunxi_engine *engine, @@ -460,14 +542,19 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine, struct drm_framebuffer *fb = plane_state->fb; struct drm_format_name_buf format_name; - if (sun4i_backend_plane_uses_frontend(plane_state)) { + if (!sun4i_backend_plane_is_supported(plane_state, + &layer_state->uses_frontend)) + return -EINVAL; + + if (layer_state->uses_frontend) { DRM_DEBUG_DRIVER("Using the frontend for plane %d\n", plane->index); - - layer_state->uses_frontend = true; num_frontend_planes++; } else { - layer_state->uses_frontend = false; + if (fb->format->is_yuv) { + DRM_DEBUG_DRIVER("Plane FB format is YUV\n"); + num_yuv_planes++; + } } DRM_DEBUG_DRIVER("Plane FB format is %s\n", @@ -476,11 +563,6 @@ static int sun4i_backend_atomic_check(struct sunxi_engine *engine, if (fb->format->has_alpha || (plane_state->alpha != DRM_BLEND_ALPHA_OPAQUE)) num_alpha_planes++; - if (fb->format->is_yuv) { - DRM_DEBUG_DRIVER("Plane FB format is YUV\n"); - num_yuv_planes++; - } - DRM_DEBUG_DRIVER("Plane zpos is %d\n", plane_state->normalized_zpos); diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.h b/drivers/gpu/drm/sun4i/sun4i_backend.h index e3d4c6035eb2..01f66463271b 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.h +++ b/drivers/gpu/drm/sun4i/sun4i_backend.h @@ -198,6 +198,7 @@ engine_to_sun4i_backend(struct sunxi_engine *engine) void sun4i_backend_layer_enable(struct sun4i_backend *backend, int layer, bool enable); +bool sun4i_backend_format_is_supported(uint32_t fmt, uint64_t modifier); int sun4i_backend_update_layer_coord(struct sun4i_backend *backend, int layer, struct drm_plane *plane); int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, @@ -208,5 +209,7 @@ int sun4i_backend_update_layer_frontend(struct sun4i_backend *backend, int layer, uint32_t in_fmt); int sun4i_backend_update_layer_zpos(struct sun4i_backend *backend, int layer, struct drm_plane *plane); +void sun4i_backend_cleanup_layer(struct sun4i_backend *backend, + int layer); #endif /* _SUN4I_BACKEND_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index ef773d36baf0..9e4c375ccc96 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -28,6 +28,16 @@ #include "sun4i_tcon.h" #include "sun8i_tcon_top.h" +static int drm_sun4i_gem_dumb_create(struct drm_file *file_priv, + struct drm_device *drm, + struct drm_mode_create_dumb *args) +{ + /* The hardware only allows even pitches for YUV buffers. */ + args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), 2); + + return drm_gem_cma_dumb_create_internal(file_priv, drm, args); +} + DEFINE_DRM_GEM_CMA_FOPS(sun4i_drv_fops); static struct drm_driver sun4i_drv_driver = { @@ -42,7 +52,7 @@ static struct drm_driver sun4i_drv_driver = { .minor = 0, /* GEM Operations */ - .dumb_create = drm_gem_cma_dumb_create, + .dumb_create = drm_sun4i_gem_dumb_create, .gem_free_object_unlocked = drm_gem_cma_free_object, .gem_vm_ops = &drm_gem_cma_vm_ops, @@ -400,6 +410,7 @@ static const struct of_device_id sun4i_drv_of_table[] = { { .compatible = "allwinner,sun8i-v3s-display-engine" }, { .compatible = "allwinner,sun9i-a80-display-engine" }, { .compatible = "allwinner,sun50i-a64-display-engine" }, + { .compatible = "allwinner,sun50i-h6-display-engine" }, { } }; MODULE_DEVICE_TABLE(of, sun4i_drv_of_table); diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.c b/drivers/gpu/drm/sun4i/sun4i_frontend.c index ddf6cfa6dd23..1a7ebc45747e 100644 --- a/drivers/gpu/drm/sun4i/sun4i_frontend.c +++ b/drivers/gpu/drm/sun4i/sun4i_frontend.c @@ -107,8 +107,34 @@ EXPORT_SYMBOL(sun4i_frontend_update_buffer); static int sun4i_frontend_drm_format_to_input_fmt(uint32_t fmt, u32 *val) { switch (fmt) { - case DRM_FORMAT_ARGB8888: - *val = 5; + case DRM_FORMAT_XRGB8888: + *val = SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_RGB; + return 0; + + default: + return -EINVAL; + } +} + +static int sun4i_frontend_drm_format_to_input_mode(uint32_t fmt, u32 *val) +{ + if (drm_format_num_planes(fmt) == 1) + *val = SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PACKED; + else + return -EINVAL; + + return 0; +} + +static int sun4i_frontend_drm_format_to_input_sequence(uint32_t fmt, u32 *val) +{ + switch (fmt) { + case DRM_FORMAT_BGRX8888: + *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_BGRX; + return 0; + + case DRM_FORMAT_XRGB8888: + *val = SUN4I_FRONTEND_INPUT_FMT_DATA_PS_XRGB; return 0; default: @@ -119,9 +145,12 @@ static int sun4i_frontend_drm_format_to_input_fmt(uint32_t fmt, u32 *val) static int sun4i_frontend_drm_format_to_output_fmt(uint32_t fmt, u32 *val) { switch (fmt) { + case DRM_FORMAT_BGRX8888: + *val = SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_BGRX8888; + return 0; + case DRM_FORMAT_XRGB8888: - case DRM_FORMAT_ARGB8888: - *val = 2; + *val = SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_XRGB8888; return 0; default: @@ -129,22 +158,54 @@ static int sun4i_frontend_drm_format_to_output_fmt(uint32_t fmt, u32 *val) } } +static const uint32_t sun4i_frontend_formats[] = { + DRM_FORMAT_BGRX8888, + DRM_FORMAT_XRGB8888, +}; + +bool sun4i_frontend_format_is_supported(uint32_t fmt, uint64_t modifier) +{ + unsigned int i; + + if (modifier != DRM_FORMAT_MOD_LINEAR) + return false; + + for (i = 0; i < ARRAY_SIZE(sun4i_frontend_formats); i++) + if (sun4i_frontend_formats[i] == fmt) + return true; + + return false; +} +EXPORT_SYMBOL(sun4i_frontend_format_is_supported); + int sun4i_frontend_update_formats(struct sun4i_frontend *frontend, struct drm_plane *plane, uint32_t out_fmt) { struct drm_plane_state *state = plane->state; struct drm_framebuffer *fb = state->fb; + uint32_t format = fb->format->format; u32 out_fmt_val; - u32 in_fmt_val; + u32 in_fmt_val, in_mod_val, in_ps_val; int ret; - ret = sun4i_frontend_drm_format_to_input_fmt(fb->format->format, - &in_fmt_val); + ret = sun4i_frontend_drm_format_to_input_fmt(format, &in_fmt_val); if (ret) { DRM_DEBUG_DRIVER("Invalid input format\n"); return ret; } + ret = sun4i_frontend_drm_format_to_input_mode(format, &in_mod_val); + if (ret) { + DRM_DEBUG_DRIVER("Invalid input mode\n"); + return ret; + } + + ret = sun4i_frontend_drm_format_to_input_sequence(format, &in_ps_val); + if (ret) { + DRM_DEBUG_DRIVER("Invalid pixel sequence\n"); + return ret; + } + ret = sun4i_frontend_drm_format_to_output_fmt(out_fmt, &out_fmt_val); if (ret) { DRM_DEBUG_DRIVER("Invalid output format\n"); @@ -162,10 +223,12 @@ int sun4i_frontend_update_formats(struct sun4i_frontend *frontend, regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTPHASE1_REG, 0x400); regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTPHASE1_REG, 0x400); + regmap_update_bits(frontend->regs, SUN4I_FRONTEND_BYPASS_REG, + SUN4I_FRONTEND_BYPASS_CSC_EN, + SUN4I_FRONTEND_BYPASS_CSC_EN); + regmap_write(frontend->regs, SUN4I_FRONTEND_INPUT_FMT_REG, - SUN4I_FRONTEND_INPUT_FMT_DATA_MOD(1) | - SUN4I_FRONTEND_INPUT_FMT_DATA_FMT(in_fmt_val) | - SUN4I_FRONTEND_INPUT_FMT_PS(1)); + in_mod_val | in_fmt_val | in_ps_val); /* * TODO: It look like the A31 and A80 at least will need the @@ -173,7 +236,7 @@ int sun4i_frontend_update_formats(struct sun4i_frontend *frontend, * ARGB8888). */ regmap_write(frontend->regs, SUN4I_FRONTEND_OUTPUT_FMT_REG, - SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT(out_fmt_val)); + out_fmt_val); return 0; } @@ -183,16 +246,24 @@ void sun4i_frontend_update_coord(struct sun4i_frontend *frontend, struct drm_plane *plane) { struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + uint32_t luma_width, luma_height; + uint32_t chroma_width, chroma_height; /* Set height and width */ DRM_DEBUG_DRIVER("Frontend size W: %u H: %u\n", state->crtc_w, state->crtc_h); + + luma_width = state->src_w >> 16; + luma_height = state->src_h >> 16; + + chroma_width = DIV_ROUND_UP(luma_width, fb->format->hsub); + chroma_height = DIV_ROUND_UP(luma_height, fb->format->vsub); + regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_INSIZE_REG, - SUN4I_FRONTEND_INSIZE(state->src_h >> 16, - state->src_w >> 16)); + SUN4I_FRONTEND_INSIZE(luma_height, luma_width)); regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_INSIZE_REG, - SUN4I_FRONTEND_INSIZE(state->src_h >> 16, - state->src_w >> 16)); + SUN4I_FRONTEND_INSIZE(chroma_height, chroma_width)); regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_OUTSIZE_REG, SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w)); @@ -200,14 +271,14 @@ void sun4i_frontend_update_coord(struct sun4i_frontend *frontend, SUN4I_FRONTEND_OUTSIZE(state->crtc_h, state->crtc_w)); regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_HORZFACT_REG, - state->src_w / state->crtc_w); + (luma_width << 16) / state->crtc_w); regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_HORZFACT_REG, - state->src_w / state->crtc_w); + (chroma_width << 16) / state->crtc_w); regmap_write(frontend->regs, SUN4I_FRONTEND_CH0_VERTFACT_REG, - state->src_h / state->crtc_h); + (luma_height << 16) / state->crtc_h); regmap_write(frontend->regs, SUN4I_FRONTEND_CH1_VERTFACT_REG, - state->src_h / state->crtc_h); + (chroma_height << 16) / state->crtc_h); regmap_write_bits(frontend->regs, SUN4I_FRONTEND_FRM_CTRL_REG, SUN4I_FRONTEND_FRM_CTRL_REG_RDY, @@ -339,10 +410,6 @@ static int sun4i_frontend_runtime_resume(struct device *dev) SUN4I_FRONTEND_EN_EN, SUN4I_FRONTEND_EN_EN); - regmap_update_bits(frontend->regs, SUN4I_FRONTEND_BYPASS_REG, - SUN4I_FRONTEND_BYPASS_CSC_EN, - SUN4I_FRONTEND_BYPASS_CSC_EN); - sun4i_frontend_scaler_init(frontend); return 0; diff --git a/drivers/gpu/drm/sun4i/sun4i_frontend.h b/drivers/gpu/drm/sun4i/sun4i_frontend.h index 02661ce81f3e..ad146e8d8d70 100644 --- a/drivers/gpu/drm/sun4i/sun4i_frontend.h +++ b/drivers/gpu/drm/sun4i/sun4i_frontend.h @@ -26,12 +26,14 @@ #define SUN4I_FRONTEND_LINESTRD0_REG 0x040 #define SUN4I_FRONTEND_INPUT_FMT_REG 0x04c -#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD(mod) ((mod) << 8) -#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT(fmt) ((fmt) << 4) -#define SUN4I_FRONTEND_INPUT_FMT_PS(ps) (ps) +#define SUN4I_FRONTEND_INPUT_FMT_DATA_MOD_PACKED (1 << 8) +#define SUN4I_FRONTEND_INPUT_FMT_DATA_FMT_RGB (5 << 4) +#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_BGRX 0 +#define SUN4I_FRONTEND_INPUT_FMT_DATA_PS_XRGB 1 #define SUN4I_FRONTEND_OUTPUT_FMT_REG 0x05c -#define SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT(fmt) (fmt) +#define SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_BGRX8888 1 +#define SUN4I_FRONTEND_OUTPUT_FMT_DATA_FMT_XRGB8888 2 #define SUN4I_FRONTEND_CH0_INSIZE_REG 0x100 #define SUN4I_FRONTEND_INSIZE(h, w) ((((h) - 1) << 16) | (((w) - 1))) @@ -95,5 +97,6 @@ void sun4i_frontend_update_coord(struct sun4i_frontend *frontend, struct drm_plane *plane); int sun4i_frontend_update_formats(struct sun4i_frontend *frontend, struct drm_plane *plane, uint32_t out_fmt); +bool sun4i_frontend_format_is_supported(uint32_t fmt, uint64_t modifier); #endif /* _SUN4I_FRONTEND_H_ */ diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c index 78f77af8805a..29631e0efde3 100644 --- a/drivers/gpu/drm/sun4i/sun4i_layer.c +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c @@ -12,6 +12,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_plane_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drmP.h> #include "sun4i_backend.h" @@ -92,14 +93,16 @@ static void sun4i_backend_layer_atomic_update(struct drm_plane *plane, struct sun4i_backend *backend = layer->backend; struct sun4i_frontend *frontend = backend->frontend; + sun4i_backend_cleanup_layer(backend, layer->id); + if (layer_state->uses_frontend) { sun4i_frontend_init(frontend); sun4i_frontend_update_coord(frontend, plane); sun4i_frontend_update_buffer(frontend, plane); sun4i_frontend_update_formats(frontend, plane, - DRM_FORMAT_ARGB8888); + DRM_FORMAT_XRGB8888); sun4i_backend_update_layer_frontend(backend, layer->id, - DRM_FORMAT_ARGB8888); + DRM_FORMAT_XRGB8888); sun4i_frontend_enable(frontend); } else { sun4i_backend_update_layer_formats(backend, layer->id, plane); @@ -112,6 +115,7 @@ static void sun4i_backend_layer_atomic_update(struct drm_plane *plane, } static const struct drm_plane_helper_funcs sun4i_backend_layer_helper_funcs = { + .prepare_fb = drm_gem_fb_prepare_fb, .atomic_disable = sun4i_backend_layer_atomic_disable, .atomic_update = sun4i_backend_layer_atomic_update, }; @@ -125,10 +129,11 @@ static const struct drm_plane_funcs sun4i_backend_layer_funcs = { .update_plane = drm_atomic_helper_update_plane, }; -static const uint32_t sun4i_backend_layer_formats[] = { +static const uint32_t sun4i_layer_formats[] = { DRM_FORMAT_ARGB8888, DRM_FORMAT_ARGB4444, DRM_FORMAT_ARGB1555, + DRM_FORMAT_BGRX8888, DRM_FORMAT_RGBA5551, DRM_FORMAT_RGBA4444, DRM_FORMAT_RGB888, @@ -154,8 +159,8 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm, /* possible crtcs are set later */ ret = drm_universal_plane_init(drm, &layer->plane, 0, &sun4i_backend_layer_funcs, - sun4i_backend_layer_formats, - ARRAY_SIZE(sun4i_backend_layer_formats), + sun4i_layer_formats, + ARRAY_SIZE(sun4i_layer_formats), NULL, type, NULL); if (ret) { dev_err(drm->dev, "Couldn't initialize layer\n"); diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index f949287d926c..0420f5c978b9 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -478,8 +478,11 @@ static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon, } static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, + const struct drm_encoder *encoder, const struct drm_display_mode *mode) { + struct drm_connector *connector = sun4i_tcon_get_connector(encoder); + struct drm_display_info display_info = connector->display_info; unsigned int bp, hsync, vsync; u8 clk_delay; u32 val = 0; @@ -491,8 +494,7 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, sun4i_tcon0_mode_set_common(tcon, mode); /* Set dithering if needed */ - if (tcon->panel) - sun4i_tcon0_mode_set_dithering(tcon, tcon->panel->connector); + sun4i_tcon0_mode_set_dithering(tcon, connector); /* Adjust clock delay */ clk_delay = sun4i_tcon_get_clk_delay(mode, 0); @@ -541,6 +543,9 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, if (mode->flags & DRM_MODE_FLAG_PVSYNC) val |= SUN4I_TCON0_IO_POL_VSYNC_POSITIVE; + if (display_info.bus_flags & DRM_BUS_FLAG_DE_LOW) + val |= SUN4I_TCON0_IO_POL_DE_NEGATIVE; + /* * On A20 and similar SoCs, the only way to achieve Positive Edge * (Rising Edge), is setting dclk clock phase to 2/3(240°). @@ -556,20 +561,16 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon, * Following code is a way to avoid quirks all around TCON * and DOTCLOCK drivers. */ - if (tcon->panel) { - struct drm_panel *panel = tcon->panel; - struct drm_connector *connector = panel->connector; - struct drm_display_info display_info = connector->display_info; + if (display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE) + clk_set_phase(tcon->dclk, 240); - if (display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE) - clk_set_phase(tcon->dclk, 240); - - if (display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE) - clk_set_phase(tcon->dclk, 0); - } + if (display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE) + clk_set_phase(tcon->dclk, 0); regmap_update_bits(tcon->regs, SUN4I_TCON0_IO_POL_REG, - SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | SUN4I_TCON0_IO_POL_VSYNC_POSITIVE, + SUN4I_TCON0_IO_POL_HSYNC_POSITIVE | + SUN4I_TCON0_IO_POL_VSYNC_POSITIVE | + SUN4I_TCON0_IO_POL_DE_NEGATIVE, val); /* Map output pins to channel 0 */ @@ -684,7 +685,7 @@ void sun4i_tcon_mode_set(struct sun4i_tcon *tcon, sun4i_tcon0_mode_set_lvds(tcon, encoder, mode); break; case DRM_MODE_ENCODER_NONE: - sun4i_tcon0_mode_set_rgb(tcon, mode); + sun4i_tcon0_mode_set_rgb(tcon, encoder, mode); sun4i_tcon_set_mux(tcon, 0, encoder); break; case DRM_MODE_ENCODER_TVDAC: diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h index 3d492c8be1fc..b5214d71610f 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.h +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h @@ -116,6 +116,7 @@ #define SUN4I_TCON0_IO_POL_REG 0x88 #define SUN4I_TCON0_IO_POL_DCLK_PHASE(phase) ((phase & 3) << 28) +#define SUN4I_TCON0_IO_POL_DE_NEGATIVE BIT(27) #define SUN4I_TCON0_IO_POL_HSYNC_POSITIVE BIT(25) #define SUN4I_TCON0_IO_POL_VSYNC_POSITIVE BIT(24) diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c index e3fc8fa920fb..18534263a05d 100644 --- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c @@ -19,6 +19,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_plane_helper.h> #include <drm/drmP.h> @@ -300,6 +301,7 @@ static void sun8i_ui_layer_atomic_update(struct drm_plane *plane, } static struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = { + .prepare_fb = drm_gem_fb_prepare_fb, .atomic_check = sun8i_ui_layer_atomic_check, .atomic_disable = sun8i_ui_layer_atomic_disable, .atomic_update = sun8i_ui_layer_atomic_update, diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c index 4249edfb47ed..87be898f9b7a 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c @@ -13,6 +13,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_plane_helper.h> #include <drm/drmP.h> @@ -336,6 +337,7 @@ static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, } static struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = { + .prepare_fb = drm_gem_fb_prepare_fb, .atomic_check = sun8i_vi_layer_atomic_check, .atomic_disable = sun8i_vi_layer_atomic_disable, .atomic_update = sun8i_vi_layer_atomic_update, diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c index 6112d9042979..3e26be5359cf 100644 --- a/drivers/gpu/drm/tegra/hub.c +++ b/drivers/gpu/drm/tegra/hub.c @@ -716,7 +716,7 @@ static int tegra_display_hub_init(struct host1x_client *client) if (!state) return -ENOMEM; - drm_atomic_private_obj_init(&hub->base, &state->base, + drm_atomic_private_obj_init(drm, &hub->base, &state->base, &tegra_display_hub_state_funcs); tegra->hub = hub; diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c index 9af51d982a33..01a6f2d42440 100644 --- a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c @@ -36,77 +36,6 @@ * and registers the DRM device using devm_tinydrm_register(). */ -/** - * tinydrm_gem_cma_prime_import_sg_table - Produce a CMA GEM object from - * another driver's scatter/gather table of pinned pages - * @drm: DRM device to import into - * @attach: DMA-BUF attachment - * @sgt: Scatter/gather table of pinned pages - * - * This function imports a scatter/gather table exported via DMA-BUF by - * another driver using drm_gem_cma_prime_import_sg_table(). It sets the - * kernel virtual address on the CMA object. Drivers should use this as their - * &drm_driver->gem_prime_import_sg_table callback if they need the virtual - * address. tinydrm_gem_cma_free_object() should be used in combination with - * this function. - * - * Returns: - * A pointer to a newly created GEM object or an ERR_PTR-encoded negative - * error code on failure. - */ -struct drm_gem_object * -tinydrm_gem_cma_prime_import_sg_table(struct drm_device *drm, - struct dma_buf_attachment *attach, - struct sg_table *sgt) -{ - struct drm_gem_cma_object *cma_obj; - struct drm_gem_object *obj; - void *vaddr; - - vaddr = dma_buf_vmap(attach->dmabuf); - if (!vaddr) { - DRM_ERROR("Failed to vmap PRIME buffer\n"); - return ERR_PTR(-ENOMEM); - } - - obj = drm_gem_cma_prime_import_sg_table(drm, attach, sgt); - if (IS_ERR(obj)) { - dma_buf_vunmap(attach->dmabuf, vaddr); - return obj; - } - - cma_obj = to_drm_gem_cma_obj(obj); - cma_obj->vaddr = vaddr; - - return obj; -} -EXPORT_SYMBOL(tinydrm_gem_cma_prime_import_sg_table); - -/** - * tinydrm_gem_cma_free_object - Free resources associated with a CMA GEM - * object - * @gem_obj: GEM object to free - * - * This function frees the backing memory of the CMA GEM object, cleans up the - * GEM object state and frees the memory used to store the object itself using - * drm_gem_cma_free_object(). It also handles PRIME buffers which has the kernel - * virtual address set by tinydrm_gem_cma_prime_import_sg_table(). Drivers - * can use this as their &drm_driver->gem_free_object_unlocked callback. - */ -void tinydrm_gem_cma_free_object(struct drm_gem_object *gem_obj) -{ - if (gem_obj->import_attach) { - struct drm_gem_cma_object *cma_obj; - - cma_obj = to_drm_gem_cma_obj(gem_obj); - dma_buf_vunmap(gem_obj->import_attach->dmabuf, cma_obj->vaddr); - cma_obj->vaddr = NULL; - } - - drm_gem_cma_free_object(gem_obj); -} -EXPORT_SYMBOL_GPL(tinydrm_gem_cma_free_object); - static struct drm_framebuffer * tinydrm_fb_create(struct drm_device *drm, struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd) diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c index dcd390163a4a..bf6bfbc5d412 100644 --- a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c @@ -9,12 +9,18 @@ #include <linux/backlight.h> #include <linux/dma-buf.h> +#include <linux/module.h> #include <linux/pm.h> #include <linux/spi/spi.h> #include <linux/swab.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_print.h> #include <drm/tinydrm/tinydrm.h> #include <drm/tinydrm/tinydrm-helpers.h> +#include <uapi/drm/drm.h> static unsigned int spi_max; module_param(spi_max, uint, 0400); diff --git a/drivers/gpu/drm/tinydrm/hx8357d.c b/drivers/gpu/drm/tinydrm/hx8357d.c index c3e51c2baebc..81a2bbeb25d4 100644 --- a/drivers/gpu/drm/tinydrm/hx8357d.c +++ b/drivers/gpu/drm/tinydrm/hx8357d.c @@ -16,7 +16,7 @@ #include <linux/property.h> #include <linux/spi/spi.h> -#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_modeset_helper.h> #include <drm/tinydrm/mipi-dbi.h> @@ -188,7 +188,7 @@ DEFINE_DRM_GEM_CMA_FOPS(hx8357d_fops); static struct drm_driver hx8357d_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, .fops = &hx8357d_fops, - TINYDRM_GEM_DRIVER_OPS, + DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "hx8357d", .desc = "HX8357D", diff --git a/drivers/gpu/drm/tinydrm/ili9225.c b/drivers/gpu/drm/tinydrm/ili9225.c index 455fefe012f5..78f7c2d1b449 100644 --- a/drivers/gpu/drm/tinydrm/ili9225.c +++ b/drivers/gpu/drm/tinydrm/ili9225.c @@ -20,7 +20,8 @@ #include <linux/spi/spi.h> #include <video/mipi_display.h> -#include <drm/drm_fb_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/tinydrm/mipi-dbi.h> #include <drm/tinydrm/tinydrm-helpers.h> @@ -367,7 +368,7 @@ static struct drm_driver ili9225_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, .fops = &ili9225_fops, - TINYDRM_GEM_DRIVER_OPS, + DRM_GEM_CMA_VMAP_DRIVER_OPS, .name = "ili9225", .desc = "Ilitek ILI9225", .date = "20171106", diff --git a/drivers/gpu/drm/tinydrm/ili9341.c b/drivers/gpu/drm/tinydrm/ili9341.c index 6701037749a7..51395bdc6ca2 100644 --- a/drivers/gpu/drm/tinydrm/ili9341.c +++ b/drivers/gpu/drm/tinydrm/ili9341.c @@ -15,7 +15,7 @@ #include <linux/property.h> #include <linux/spi/spi.h> -#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_modeset_helper.h> #include <drm/tinydrm/mipi-dbi.h> @@ -144,7 +144,7 @@ DEFINE_DRM_GEM_CMA_FOPS(ili9341_fops); static struct drm_driver ili9341_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, .fops = &ili9341_fops, - TINYDRM_GEM_DRIVER_OPS, + DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "ili9341", .desc = "Ilitek ILI9341", diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c index d7bb4c5e6657..3fa62e77c30b 100644 --- a/drivers/gpu/drm/tinydrm/mi0283qt.c +++ b/drivers/gpu/drm/tinydrm/mi0283qt.c @@ -17,9 +17,9 @@ #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> -#include <drm/drm_fb_helper.h> -#include <drm/drm_modeset_helper.h> +#include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_modeset_helper.h> #include <drm/tinydrm/mipi-dbi.h> #include <drm/tinydrm/tinydrm-helpers.h> #include <video/mipi_display.h> @@ -153,7 +153,7 @@ static struct drm_driver mi0283qt_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, .fops = &mi0283qt_fops, - TINYDRM_GEM_DRIVER_OPS, + DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "mi0283qt", .desc = "Multi-Inno MI0283QT", diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c index 1bb870021f6e..3a05e56f9b0d 100644 --- a/drivers/gpu/drm/tinydrm/mipi-dbi.c +++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c @@ -9,15 +9,19 @@ * (at your option) any later version. */ -#include <drm/drm_gem_framebuffer_helper.h> -#include <drm/tinydrm/mipi-dbi.h> -#include <drm/tinydrm/tinydrm-helpers.h> #include <linux/debugfs.h> #include <linux/dma-buf.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> + +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/tinydrm/mipi-dbi.h> +#include <drm/tinydrm/tinydrm-helpers.h> +#include <uapi/drm/drm.h> #include <video/mipi_display.h> #define MIPI_DBI_MAX_SPI_READ_SPEED 2000000 /* 2MHz */ diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c index 50a1d4216ce7..54d6fe0f37ce 100644 --- a/drivers/gpu/drm/tinydrm/repaper.c +++ b/drivers/gpu/drm/tinydrm/repaper.c @@ -26,6 +26,8 @@ #include <linux/spi/spi.h> #include <linux/thermal.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/tinydrm/tinydrm.h> #include <drm/tinydrm/tinydrm-helpers.h> @@ -106,12 +108,11 @@ static int repaper_spi_transfer(struct spi_device *spi, u8 header, /* Stack allocated tx? */ if (tx && len <= 32) { - txbuf = kmalloc(len, GFP_KERNEL); + txbuf = kmemdup(tx, len, GFP_KERNEL); if (!txbuf) { ret = -ENOMEM; goto out_free; } - memcpy(txbuf, tx, len); } if (rx) { @@ -882,7 +883,7 @@ static struct drm_driver repaper_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, .fops = &repaper_fops, - TINYDRM_GEM_DRIVER_OPS, + DRM_GEM_CMA_VMAP_DRIVER_OPS, .name = "repaper", .desc = "Pervasive Displays RePaper e-ink panels", .date = "20170405", diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c index 2fcbc3067d71..a6a8a1081b73 100644 --- a/drivers/gpu/drm/tinydrm/st7586.c +++ b/drivers/gpu/drm/tinydrm/st7586.c @@ -17,7 +17,8 @@ #include <linux/spi/spi.h> #include <video/mipi_display.h> -#include <drm/drm_fb_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/tinydrm/mipi-dbi.h> #include <drm/tinydrm/tinydrm-helpers.h> @@ -303,7 +304,7 @@ static struct drm_driver st7586_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, .fops = &st7586_fops, - TINYDRM_GEM_DRIVER_OPS, + DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "st7586", .desc = "Sitronix ST7586", diff --git a/drivers/gpu/drm/tinydrm/st7735r.c b/drivers/gpu/drm/tinydrm/st7735r.c index 3081bc57c116..b39779e0dcd8 100644 --- a/drivers/gpu/drm/tinydrm/st7735r.c +++ b/drivers/gpu/drm/tinydrm/st7735r.c @@ -14,7 +14,7 @@ #include <linux/spi/spi.h> #include <video/mipi_display.h> -#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_cma_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/tinydrm/mipi-dbi.h> #include <drm/tinydrm/tinydrm-helpers.h> @@ -119,7 +119,7 @@ static struct drm_driver st7735r_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, .fops = &st7735r_fops, - TINYDRM_GEM_DRIVER_OPS, + DRM_GEM_CMA_VMAP_DRIVER_OPS, .debugfs_init = mipi_dbi_debugfs_init, .name = "st7735r", .desc = "Sitronix ST7735R", diff --git a/drivers/gpu/drm/tve200/tve200_drv.c b/drivers/gpu/drm/tve200/tve200_drv.c index 72efcecb44f7..28e2d03c0ccf 100644 --- a/drivers/gpu/drm/tve200/tve200_drv.c +++ b/drivers/gpu/drm/tve200/tve200_drv.c @@ -249,7 +249,7 @@ static int tve200_probe(struct platform_device *pdev) clk_disable: clk_disable_unprepare(priv->pclk); dev_unref: - drm_dev_unref(drm); + drm_dev_put(drm); return ret; } @@ -263,7 +263,7 @@ static int tve200_remove(struct platform_device *pdev) drm_panel_bridge_remove(priv->bridge); drm_mode_config_cleanup(drm); clk_disable_unprepare(priv->pclk); - drm_dev_unref(drm); + drm_dev_put(drm); return 0; } diff --git a/drivers/gpu/drm/v3d/v3d_bo.c b/drivers/gpu/drm/v3d/v3d_bo.c index 54d96518a131..a08766d39eab 100644 --- a/drivers/gpu/drm/v3d/v3d_bo.c +++ b/drivers/gpu/drm/v3d/v3d_bo.c @@ -293,6 +293,7 @@ v3d_prime_import_sg_table(struct drm_device *dev, bo->resv = attach->dmabuf->resv; bo->sgt = sgt; + obj->import_attach = attach; v3d_bo_get_pages(bo); v3d_mmu_insert_ptes(bo); diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c index 2a85fa68ffea..f0afcec72c34 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.c +++ b/drivers/gpu/drm/v3d/v3d_drv.c @@ -112,10 +112,15 @@ static int v3d_get_param_ioctl(struct drm_device *dev, void *data, return 0; } - /* Any params that aren't just register reads would go here. */ - DRM_DEBUG("Unknown parameter %d\n", args->param); - return -EINVAL; + switch (args->param) { + case DRM_V3D_PARAM_SUPPORTS_TFU: + args->value = 1; + return 0; + default: + DRM_DEBUG("Unknown parameter %d\n", args->param); + return -EINVAL; + } } static int @@ -170,7 +175,8 @@ static const struct file_operations v3d_drm_fops = { /* DRM_AUTH is required on SUBMIT_CL for now, while we don't have GMP * protection between clients. Note that render nodes would be be * able to submit CLs that could access BOs from clients authenticated - * with the master node. + * with the master node. The TFU doesn't use the GMP, so it would + * need to stay DRM_AUTH until we do buffer size/offset validation. */ static const struct drm_ioctl_desc v3d_drm_ioctls[] = { DRM_IOCTL_DEF_DRV(V3D_SUBMIT_CL, v3d_submit_cl_ioctl, DRM_RENDER_ALLOW | DRM_AUTH), @@ -179,6 +185,7 @@ static const struct drm_ioctl_desc v3d_drm_ioctls[] = { DRM_IOCTL_DEF_DRV(V3D_MMAP_BO, v3d_mmap_bo_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(V3D_GET_PARAM, v3d_get_param_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(V3D_GET_BO_OFFSET, v3d_get_bo_offset_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(V3D_SUBMIT_TFU, v3d_submit_tfu_ioctl, DRM_RENDER_ALLOW | DRM_AUTH), }; static const struct vm_operations_struct v3d_vm_ops = { diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index cbe5be0c47eb..fdda3037f7af 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -7,19 +7,18 @@ #include <drm/drm_encoder.h> #include <drm/drm_gem.h> #include <drm/gpu_scheduler.h> +#include "uapi/drm/v3d_drm.h" #define GMP_GRANULARITY (128 * 1024) -/* Enum for each of the V3D queues. We maintain various queue - * tracking as an array because at some point we'll want to support - * the TFU (texture formatting unit) as another queue. - */ +/* Enum for each of the V3D queues. */ enum v3d_queue { V3D_BIN, V3D_RENDER, + V3D_TFU, }; -#define V3D_MAX_QUEUES (V3D_RENDER + 1) +#define V3D_MAX_QUEUES (V3D_TFU + 1) struct v3d_queue_state { struct drm_gpu_scheduler sched; @@ -68,6 +67,7 @@ struct v3d_dev { struct v3d_exec_info *bin_job; struct v3d_exec_info *render_job; + struct v3d_tfu_job *tfu_job; struct v3d_queue_state queue[V3D_MAX_QUEUES]; @@ -218,6 +218,25 @@ struct v3d_exec_info { u32 qma, qms, qts; }; +struct v3d_tfu_job { + struct drm_sched_job base; + + struct drm_v3d_submit_tfu args; + + /* An optional fence userspace can pass in for the job to depend on. */ + struct dma_fence *in_fence; + + /* v3d fence to be signaled by IRQ handler when the job is complete. */ + struct dma_fence *done_fence; + + struct v3d_dev *v3d; + + struct kref refcount; + + /* This is the array of BOs that were looked up at the start of exec. */ + struct v3d_bo *bo[4]; +}; + /** * _wait_for - magic (register) wait macro * @@ -281,12 +300,14 @@ int v3d_gem_init(struct drm_device *dev); void v3d_gem_destroy(struct drm_device *dev); int v3d_submit_cl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); int v3d_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void v3d_exec_put(struct v3d_exec_info *exec); +void v3d_tfu_job_put(struct v3d_tfu_job *exec); void v3d_reset(struct v3d_dev *v3d); void v3d_invalidate_caches(struct v3d_dev *v3d); -void v3d_flush_caches(struct v3d_dev *v3d); /* v3d_irq.c */ void v3d_irq_init(struct v3d_dev *v3d); diff --git a/drivers/gpu/drm/v3d/v3d_fence.c b/drivers/gpu/drm/v3d/v3d_fence.c index 50bfcf9a8a1a..b0a2a1ae2eb1 100644 --- a/drivers/gpu/drm/v3d/v3d_fence.c +++ b/drivers/gpu/drm/v3d/v3d_fence.c @@ -29,10 +29,16 @@ static const char *v3d_fence_get_timeline_name(struct dma_fence *fence) { struct v3d_fence *f = to_v3d_fence(fence); - if (f->queue == V3D_BIN) + switch (f->queue) { + case V3D_BIN: return "v3d-bin"; - else + case V3D_RENDER: return "v3d-render"; + case V3D_TFU: + return "v3d-tfu"; + default: + return NULL; + } } const struct dma_fence_ops v3d_fence_ops = { diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index b88c96911453..803f31467ec1 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -130,38 +130,31 @@ v3d_flush_l3(struct v3d_dev *v3d) } } -/* Invalidates the (read-only) L2 cache. */ +/* Invalidates the (read-only) L2C cache. This was the L2 cache for + * uniforms and instructions on V3D 3.2. + */ static void -v3d_invalidate_l2(struct v3d_dev *v3d, int core) +v3d_invalidate_l2c(struct v3d_dev *v3d, int core) { + if (v3d->ver > 32) + return; + V3D_CORE_WRITE(core, V3D_CTL_L2CACTL, V3D_L2CACTL_L2CCLR | V3D_L2CACTL_L2CENA); } -static void -v3d_invalidate_l1td(struct v3d_dev *v3d, int core) -{ - V3D_CORE_WRITE(core, V3D_CTL_L2TCACTL, V3D_L2TCACTL_TMUWCF); - if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) & - V3D_L2TCACTL_L2TFLS), 100)) { - DRM_ERROR("Timeout waiting for L1T write combiner flush\n"); - } -} - /* Invalidates texture L2 cachelines */ static void v3d_flush_l2t(struct v3d_dev *v3d, int core) { - v3d_invalidate_l1td(v3d, core); - + /* While there is a busy bit (V3D_L2TCACTL_L2TFLS), we don't + * need to wait for completion before dispatching the job -- + * L2T accesses will be stalled until the flush has completed. + */ V3D_CORE_WRITE(core, V3D_CTL_L2TCACTL, V3D_L2TCACTL_L2TFLS | V3D_SET_FIELD(V3D_L2TCACTL_FLM_FLUSH, V3D_L2TCACTL_FLM)); - if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) & - V3D_L2TCACTL_L2TFLS), 100)) { - DRM_ERROR("Timeout waiting for L2T flush\n"); - } } /* Invalidates the slice caches. These are read-only caches. */ @@ -175,64 +168,41 @@ v3d_invalidate_slices(struct v3d_dev *v3d, int core) V3D_SET_FIELD(0xf, V3D_SLCACTL_ICC)); } -/* Invalidates texture L2 cachelines */ -static void -v3d_invalidate_l2t(struct v3d_dev *v3d, int core) -{ - V3D_CORE_WRITE(core, - V3D_CTL_L2TCACTL, - V3D_L2TCACTL_L2TFLS | - V3D_SET_FIELD(V3D_L2TCACTL_FLM_CLEAR, V3D_L2TCACTL_FLM)); - if (wait_for(!(V3D_CORE_READ(core, V3D_CTL_L2TCACTL) & - V3D_L2TCACTL_L2TFLS), 100)) { - DRM_ERROR("Timeout waiting for L2T invalidate\n"); - } -} - void v3d_invalidate_caches(struct v3d_dev *v3d) { + /* Invalidate the caches from the outside in. That way if + * another CL's concurrent use of nearby memory were to pull + * an invalidated cacheline back in, we wouldn't leave stale + * data in the inner cache. + */ v3d_flush_l3(v3d); - - v3d_invalidate_l2(v3d, 0); - v3d_invalidate_slices(v3d, 0); + v3d_invalidate_l2c(v3d, 0); v3d_flush_l2t(v3d, 0); -} - -void -v3d_flush_caches(struct v3d_dev *v3d) -{ - v3d_invalidate_l1td(v3d, 0); - v3d_invalidate_l2t(v3d, 0); + v3d_invalidate_slices(v3d, 0); } static void -v3d_attach_object_fences(struct v3d_exec_info *exec) +v3d_attach_object_fences(struct v3d_bo **bos, int bo_count, + struct dma_fence *fence) { - struct dma_fence *out_fence = exec->render_done_fence; - struct v3d_bo *bo; int i; - for (i = 0; i < exec->bo_count; i++) { - bo = to_v3d_bo(&exec->bo[i]->base); - + for (i = 0; i < bo_count; i++) { /* XXX: Use shared fences for read-only objects. */ - reservation_object_add_excl_fence(bo->resv, out_fence); + reservation_object_add_excl_fence(bos[i]->resv, fence); } } static void -v3d_unlock_bo_reservations(struct drm_device *dev, - struct v3d_exec_info *exec, +v3d_unlock_bo_reservations(struct v3d_bo **bos, + int bo_count, struct ww_acquire_ctx *acquire_ctx) { int i; - for (i = 0; i < exec->bo_count; i++) { - struct v3d_bo *bo = to_v3d_bo(&exec->bo[i]->base); - - ww_mutex_unlock(&bo->resv->lock); - } + for (i = 0; i < bo_count; i++) + ww_mutex_unlock(&bos[i]->resv->lock); ww_acquire_fini(acquire_ctx); } @@ -245,19 +215,19 @@ v3d_unlock_bo_reservations(struct drm_device *dev, * to v3d, so we don't attach dma-buf fences to them. */ static int -v3d_lock_bo_reservations(struct drm_device *dev, - struct v3d_exec_info *exec, +v3d_lock_bo_reservations(struct v3d_bo **bos, + int bo_count, struct ww_acquire_ctx *acquire_ctx) { int contended_lock = -1; int i, ret; - struct v3d_bo *bo; ww_acquire_init(acquire_ctx, &reservation_ww_class); retry: if (contended_lock != -1) { - bo = to_v3d_bo(&exec->bo[contended_lock]->base); + struct v3d_bo *bo = bos[contended_lock]; + ret = ww_mutex_lock_slow_interruptible(&bo->resv->lock, acquire_ctx); if (ret) { @@ -266,23 +236,20 @@ retry: } } - for (i = 0; i < exec->bo_count; i++) { + for (i = 0; i < bo_count; i++) { if (i == contended_lock) continue; - bo = to_v3d_bo(&exec->bo[i]->base); - - ret = ww_mutex_lock_interruptible(&bo->resv->lock, acquire_ctx); + ret = ww_mutex_lock_interruptible(&bos[i]->resv->lock, + acquire_ctx); if (ret) { int j; - for (j = 0; j < i; j++) { - bo = to_v3d_bo(&exec->bo[j]->base); - ww_mutex_unlock(&bo->resv->lock); - } + for (j = 0; j < i; j++) + ww_mutex_unlock(&bos[j]->resv->lock); if (contended_lock != -1 && contended_lock >= i) { - bo = to_v3d_bo(&exec->bo[contended_lock]->base); + struct v3d_bo *bo = bos[contended_lock]; ww_mutex_unlock(&bo->resv->lock); } @@ -302,12 +269,11 @@ retry: /* Reserve space for our shared (read-only) fence references, * before we commit the CL to the hardware. */ - for (i = 0; i < exec->bo_count; i++) { - bo = to_v3d_bo(&exec->bo[i]->base); - - ret = reservation_object_reserve_shared(bo->resv, 1); + for (i = 0; i < bo_count; i++) { + ret = reservation_object_reserve_shared(bos[i]->resv, 1); if (ret) { - v3d_unlock_bo_reservations(dev, exec, acquire_ctx); + v3d_unlock_bo_reservations(bos, bo_count, + acquire_ctx); return ret; } } @@ -430,6 +396,33 @@ void v3d_exec_put(struct v3d_exec_info *exec) kref_put(&exec->refcount, v3d_exec_cleanup); } +static void +v3d_tfu_job_cleanup(struct kref *ref) +{ + struct v3d_tfu_job *job = container_of(ref, struct v3d_tfu_job, + refcount); + struct v3d_dev *v3d = job->v3d; + unsigned int i; + + dma_fence_put(job->in_fence); + dma_fence_put(job->done_fence); + + for (i = 0; i < ARRAY_SIZE(job->bo); i++) { + if (job->bo[i]) + drm_gem_object_put_unlocked(&job->bo[i]->base); + } + + pm_runtime_mark_last_busy(v3d->dev); + pm_runtime_put_autosuspend(v3d->dev); + + kfree(job); +} + +void v3d_tfu_job_put(struct v3d_tfu_job *job) +{ + kref_put(&job->refcount, v3d_tfu_job_cleanup); +} + int v3d_wait_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -504,6 +497,8 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, struct drm_syncobj *sync_out; int ret = 0; + trace_v3d_submit_cl_ioctl(&v3d->drm, args->rcl_start, args->rcl_end); + if (args->pad != 0) { DRM_INFO("pad must be zero: %d\n", args->pad); return -EINVAL; @@ -547,7 +542,8 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, if (ret) goto fail; - ret = v3d_lock_bo_reservations(dev, exec, &acquire_ctx); + ret = v3d_lock_bo_reservations(exec->bo, exec->bo_count, + &acquire_ctx); if (ret) goto fail; @@ -581,15 +577,15 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, &v3d_priv->sched_entity[V3D_RENDER]); mutex_unlock(&v3d->sched_lock); - v3d_attach_object_fences(exec); + v3d_attach_object_fences(exec->bo, exec->bo_count, + exec->render_done_fence); - v3d_unlock_bo_reservations(dev, exec, &acquire_ctx); + v3d_unlock_bo_reservations(exec->bo, exec->bo_count, &acquire_ctx); /* Update the return sync object for the */ sync_out = drm_syncobj_find(file_priv, args->out_sync); if (sync_out) { - drm_syncobj_replace_fence(sync_out, 0, - exec->render_done_fence); + drm_syncobj_replace_fence(sync_out, exec->render_done_fence); drm_syncobj_put(sync_out); } @@ -599,13 +595,121 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, fail_unreserve: mutex_unlock(&v3d->sched_lock); - v3d_unlock_bo_reservations(dev, exec, &acquire_ctx); + v3d_unlock_bo_reservations(exec->bo, exec->bo_count, &acquire_ctx); fail: v3d_exec_put(exec); return ret; } +/** + * v3d_submit_tfu_ioctl() - Submits a TFU (texture formatting) job to the V3D. + * @dev: DRM device + * @data: ioctl argument + * @file_priv: DRM file for this fd + * + * Userspace provides the register setup for the TFU, which we don't + * need to validate since the TFU is behind the MMU. + */ +int +v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct v3d_dev *v3d = to_v3d_dev(dev); + struct v3d_file_priv *v3d_priv = file_priv->driver_priv; + struct drm_v3d_submit_tfu *args = data; + struct v3d_tfu_job *job; + struct ww_acquire_ctx acquire_ctx; + struct drm_syncobj *sync_out; + struct dma_fence *sched_done_fence; + int ret = 0; + int bo_count; + + trace_v3d_submit_tfu_ioctl(&v3d->drm, args->iia); + + job = kcalloc(1, sizeof(*job), GFP_KERNEL); + if (!job) + return -ENOMEM; + + ret = pm_runtime_get_sync(v3d->dev); + if (ret < 0) { + kfree(job); + return ret; + } + + kref_init(&job->refcount); + + ret = drm_syncobj_find_fence(file_priv, args->in_sync, + 0, 0, &job->in_fence); + if (ret == -EINVAL) + goto fail; + + job->args = *args; + job->v3d = v3d; + + spin_lock(&file_priv->table_lock); + for (bo_count = 0; bo_count < ARRAY_SIZE(job->bo); bo_count++) { + struct drm_gem_object *bo; + + if (!args->bo_handles[bo_count]) + break; + + bo = idr_find(&file_priv->object_idr, + args->bo_handles[bo_count]); + if (!bo) { + DRM_DEBUG("Failed to look up GEM BO %d: %d\n", + bo_count, args->bo_handles[bo_count]); + ret = -ENOENT; + spin_unlock(&file_priv->table_lock); + goto fail; + } + drm_gem_object_get(bo); + job->bo[bo_count] = to_v3d_bo(bo); + } + spin_unlock(&file_priv->table_lock); + + ret = v3d_lock_bo_reservations(job->bo, bo_count, &acquire_ctx); + if (ret) + goto fail; + + mutex_lock(&v3d->sched_lock); + ret = drm_sched_job_init(&job->base, + &v3d_priv->sched_entity[V3D_TFU], + v3d_priv); + if (ret) + goto fail_unreserve; + + sched_done_fence = dma_fence_get(&job->base.s_fence->finished); + + kref_get(&job->refcount); /* put by scheduler job completion */ + drm_sched_entity_push_job(&job->base, &v3d_priv->sched_entity[V3D_TFU]); + mutex_unlock(&v3d->sched_lock); + + v3d_attach_object_fences(job->bo, bo_count, sched_done_fence); + + v3d_unlock_bo_reservations(job->bo, bo_count, &acquire_ctx); + + /* Update the return sync object */ + sync_out = drm_syncobj_find(file_priv, args->out_sync); + if (sync_out) { + drm_syncobj_replace_fence(sync_out, sched_done_fence); + drm_syncobj_put(sync_out); + } + dma_fence_put(sched_done_fence); + + v3d_tfu_job_put(job); + + return 0; + +fail_unreserve: + mutex_unlock(&v3d->sched_lock); + v3d_unlock_bo_reservations(job->bo, bo_count, &acquire_ctx); +fail: + v3d_tfu_job_put(job); + + return ret; +} + int v3d_gem_init(struct drm_device *dev) { diff --git a/drivers/gpu/drm/v3d/v3d_irq.c b/drivers/gpu/drm/v3d/v3d_irq.c index e07514eb11b5..69338da70ddc 100644 --- a/drivers/gpu/drm/v3d/v3d_irq.c +++ b/drivers/gpu/drm/v3d/v3d_irq.c @@ -4,8 +4,8 @@ /** * DOC: Interrupt management for the V3D engine * - * When we take a binning or rendering flush done interrupt, we need - * to signal the fence for that job so that the scheduler can queue up + * When we take a bin, render, or TFU done interrupt, we need to + * signal the fence for that job so that the scheduler can queue up * the next one and unblock any waiters. * * When we take the binner out of memory interrupt, we need to @@ -15,6 +15,7 @@ #include "v3d_drv.h" #include "v3d_regs.h" +#include "v3d_trace.h" #define V3D_CORE_IRQS ((u32)(V3D_INT_OUTOMEM | \ V3D_INT_FLDONE | \ @@ -23,7 +24,8 @@ #define V3D_HUB_IRQS ((u32)(V3D_HUB_INT_MMU_WRV | \ V3D_HUB_INT_MMU_PTI | \ - V3D_HUB_INT_MMU_CAP)) + V3D_HUB_INT_MMU_CAP | \ + V3D_HUB_INT_TFUC)) static void v3d_overflow_mem_work(struct work_struct *work) @@ -87,12 +89,20 @@ v3d_irq(int irq, void *arg) } if (intsts & V3D_INT_FLDONE) { - dma_fence_signal(v3d->bin_job->bin.done_fence); + struct v3d_fence *fence = + to_v3d_fence(v3d->bin_job->bin.done_fence); + + trace_v3d_bcl_irq(&v3d->drm, fence->seqno); + dma_fence_signal(&fence->base); status = IRQ_HANDLED; } if (intsts & V3D_INT_FRDONE) { - dma_fence_signal(v3d->render_job->render.done_fence); + struct v3d_fence *fence = + to_v3d_fence(v3d->render_job->render.done_fence); + + trace_v3d_rcl_irq(&v3d->drm, fence->seqno); + dma_fence_signal(&fence->base); status = IRQ_HANDLED; } @@ -117,6 +127,15 @@ v3d_hub_irq(int irq, void *arg) /* Acknowledge the interrupts we're handling here. */ V3D_WRITE(V3D_HUB_INT_CLR, intsts); + if (intsts & V3D_HUB_INT_TFUC) { + struct v3d_fence *fence = + to_v3d_fence(v3d->tfu_job->done_fence); + + trace_v3d_tfu_irq(&v3d->drm, fence->seqno); + dma_fence_signal(&fence->base); + status = IRQ_HANDLED; + } + if (intsts & (V3D_HUB_INT_MMU_WRV | V3D_HUB_INT_MMU_PTI | V3D_HUB_INT_MMU_CAP)) { diff --git a/drivers/gpu/drm/v3d/v3d_regs.h b/drivers/gpu/drm/v3d/v3d_regs.h index c3a5e4e44f73..6ccdee9d47bd 100644 --- a/drivers/gpu/drm/v3d/v3d_regs.h +++ b/drivers/gpu/drm/v3d/v3d_regs.h @@ -86,6 +86,55 @@ # define V3D_TOP_GR_BRIDGE_SW_INIT_1 0x0000c # define V3D_TOP_GR_BRIDGE_SW_INIT_1_V3D_CLK_108_SW_INIT BIT(0) +#define V3D_TFU_CS 0x00400 +/* Stops current job, empties input fifo. */ +# define V3D_TFU_CS_TFURST BIT(31) +# define V3D_TFU_CS_CVTCT_MASK V3D_MASK(23, 16) +# define V3D_TFU_CS_CVTCT_SHIFT 16 +# define V3D_TFU_CS_NFREE_MASK V3D_MASK(13, 8) +# define V3D_TFU_CS_NFREE_SHIFT 8 +# define V3D_TFU_CS_BUSY BIT(0) + +#define V3D_TFU_SU 0x00404 +/* Interrupt when FINTTHR input slots are free (0 = disabled) */ +# define V3D_TFU_SU_FINTTHR_MASK V3D_MASK(13, 8) +# define V3D_TFU_SU_FINTTHR_SHIFT 8 +/* Skips resetting the CRC at the start of CRC generation. */ +# define V3D_TFU_SU_CRCCHAIN BIT(4) +/* skips writes, computes CRC of the image. miplevels must be 0. */ +# define V3D_TFU_SU_CRC BIT(3) +# define V3D_TFU_SU_THROTTLE_MASK V3D_MASK(1, 0) +# define V3D_TFU_SU_THROTTLE_SHIFT 0 + +#define V3D_TFU_ICFG 0x00408 +/* Interrupt when the conversion is complete. */ +# define V3D_TFU_ICFG_IOC BIT(0) + +/* Input Image Address */ +#define V3D_TFU_IIA 0x0040c +/* Input Chroma Address */ +#define V3D_TFU_ICA 0x00410 +/* Input Image Stride */ +#define V3D_TFU_IIS 0x00414 +/* Input Image U-Plane Address */ +#define V3D_TFU_IUA 0x00418 +/* Output Image Address */ +#define V3D_TFU_IOA 0x0041c +/* Image Output Size */ +#define V3D_TFU_IOS 0x00420 +/* TFU YUV Coefficient 0 */ +#define V3D_TFU_COEF0 0x00424 +/* Use these regs instead of the defaults. */ +# define V3D_TFU_COEF0_USECOEF BIT(31) +/* TFU YUV Coefficient 1 */ +#define V3D_TFU_COEF1 0x00428 +/* TFU YUV Coefficient 2 */ +#define V3D_TFU_COEF2 0x0042c +/* TFU YUV Coefficient 3 */ +#define V3D_TFU_COEF3 0x00430 + +#define V3D_TFU_CRC 0x00434 + /* Per-MMU registers. */ #define V3D_MMUC_CONTROL 0x01000 diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index 445b2ef03303..f7508e907536 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -30,6 +30,12 @@ to_v3d_job(struct drm_sched_job *sched_job) return container_of(sched_job, struct v3d_job, base); } +static struct v3d_tfu_job * +to_tfu_job(struct drm_sched_job *sched_job) +{ + return container_of(sched_job, struct v3d_tfu_job, base); +} + static void v3d_job_free(struct drm_sched_job *sched_job) { @@ -40,8 +46,18 @@ v3d_job_free(struct drm_sched_job *sched_job) v3d_exec_put(job->exec); } +static void +v3d_tfu_job_free(struct drm_sched_job *sched_job) +{ + struct v3d_tfu_job *job = to_tfu_job(sched_job); + + drm_sched_job_cleanup(sched_job); + + v3d_tfu_job_put(job); +} + /** - * Returns the fences that the bin job depends on, one by one. + * Returns the fences that the bin or render job depends on, one by one. * v3d_job_run() won't be called until all of them have been signaled. */ static struct dma_fence * @@ -78,6 +94,27 @@ v3d_job_dependency(struct drm_sched_job *sched_job, return fence; } +/** + * Returns the fences that the TFU job depends on, one by one. + * v3d_tfu_job_run() won't be called until all of them have been + * signaled. + */ +static struct dma_fence * +v3d_tfu_job_dependency(struct drm_sched_job *sched_job, + struct drm_sched_entity *s_entity) +{ + struct v3d_tfu_job *job = to_tfu_job(sched_job); + struct dma_fence *fence; + + fence = job->in_fence; + if (fence) { + job->in_fence = NULL; + return fence; + } + + return NULL; +} + static struct dma_fence *v3d_job_run(struct drm_sched_job *sched_job) { struct v3d_job *job = to_v3d_job(sched_job); @@ -149,28 +186,47 @@ static struct dma_fence *v3d_job_run(struct drm_sched_job *sched_job) return fence; } -static void -v3d_job_timedout(struct drm_sched_job *sched_job) +static struct dma_fence * +v3d_tfu_job_run(struct drm_sched_job *sched_job) { - struct v3d_job *job = to_v3d_job(sched_job); - struct v3d_exec_info *exec = job->exec; - struct v3d_dev *v3d = exec->v3d; - enum v3d_queue job_q = job == &exec->bin ? V3D_BIN : V3D_RENDER; - enum v3d_queue q; - u32 ctca = V3D_CORE_READ(0, V3D_CLE_CTNCA(job_q)); - u32 ctra = V3D_CORE_READ(0, V3D_CLE_CTNRA(job_q)); + struct v3d_tfu_job *job = to_tfu_job(sched_job); + struct v3d_dev *v3d = job->v3d; + struct drm_device *dev = &v3d->drm; + struct dma_fence *fence; - /* If the current address or return address have changed, then - * the GPU has probably made progress and we should delay the - * reset. This could fail if the GPU got in an infinite loop - * in the CL, but that is pretty unlikely outside of an i-g-t - * testcase. - */ - if (job->timedout_ctca != ctca || job->timedout_ctra != ctra) { - job->timedout_ctca = ctca; - job->timedout_ctra = ctra; - return; + fence = v3d_fence_create(v3d, V3D_TFU); + if (IS_ERR(fence)) + return NULL; + + v3d->tfu_job = job; + if (job->done_fence) + dma_fence_put(job->done_fence); + job->done_fence = dma_fence_get(fence); + + trace_v3d_submit_tfu(dev, to_v3d_fence(fence)->seqno); + + V3D_WRITE(V3D_TFU_IIA, job->args.iia); + V3D_WRITE(V3D_TFU_IIS, job->args.iis); + V3D_WRITE(V3D_TFU_ICA, job->args.ica); + V3D_WRITE(V3D_TFU_IUA, job->args.iua); + V3D_WRITE(V3D_TFU_IOA, job->args.ioa); + V3D_WRITE(V3D_TFU_IOS, job->args.ios); + V3D_WRITE(V3D_TFU_COEF0, job->args.coef[0]); + if (job->args.coef[0] & V3D_TFU_COEF0_USECOEF) { + V3D_WRITE(V3D_TFU_COEF1, job->args.coef[1]); + V3D_WRITE(V3D_TFU_COEF2, job->args.coef[2]); + V3D_WRITE(V3D_TFU_COEF3, job->args.coef[3]); } + /* ICFG kicks off the job. */ + V3D_WRITE(V3D_TFU_ICFG, job->args.icfg | V3D_TFU_ICFG_IOC); + + return fence; +} + +static void +v3d_gpu_reset_for_timeout(struct v3d_dev *v3d, struct drm_sched_job *sched_job) +{ + enum v3d_queue q; mutex_lock(&v3d->reset_lock); @@ -195,6 +251,39 @@ v3d_job_timedout(struct drm_sched_job *sched_job) mutex_unlock(&v3d->reset_lock); } +static void +v3d_job_timedout(struct drm_sched_job *sched_job) +{ + struct v3d_job *job = to_v3d_job(sched_job); + struct v3d_exec_info *exec = job->exec; + struct v3d_dev *v3d = exec->v3d; + enum v3d_queue job_q = job == &exec->bin ? V3D_BIN : V3D_RENDER; + u32 ctca = V3D_CORE_READ(0, V3D_CLE_CTNCA(job_q)); + u32 ctra = V3D_CORE_READ(0, V3D_CLE_CTNRA(job_q)); + + /* If the current address or return address have changed, then + * the GPU has probably made progress and we should delay the + * reset. This could fail if the GPU got in an infinite loop + * in the CL, but that is pretty unlikely outside of an i-g-t + * testcase. + */ + if (job->timedout_ctca != ctca || job->timedout_ctra != ctra) { + job->timedout_ctca = ctca; + job->timedout_ctra = ctra; + return; + } + + v3d_gpu_reset_for_timeout(v3d, sched_job); +} + +static void +v3d_tfu_job_timedout(struct drm_sched_job *sched_job) +{ + struct v3d_tfu_job *job = to_tfu_job(sched_job); + + v3d_gpu_reset_for_timeout(job->v3d, sched_job); +} + static const struct drm_sched_backend_ops v3d_sched_ops = { .dependency = v3d_job_dependency, .run_job = v3d_job_run, @@ -202,6 +291,13 @@ static const struct drm_sched_backend_ops v3d_sched_ops = { .free_job = v3d_job_free }; +static const struct drm_sched_backend_ops v3d_tfu_sched_ops = { + .dependency = v3d_tfu_job_dependency, + .run_job = v3d_tfu_job_run, + .timedout_job = v3d_tfu_job_timedout, + .free_job = v3d_tfu_job_free +}; + int v3d_sched_init(struct v3d_dev *v3d) { @@ -232,6 +328,19 @@ v3d_sched_init(struct v3d_dev *v3d) return ret; } + ret = drm_sched_init(&v3d->queue[V3D_TFU].sched, + &v3d_tfu_sched_ops, + hw_jobs_limit, job_hang_limit, + msecs_to_jiffies(hang_limit_ms), + "v3d_tfu"); + if (ret) { + dev_err(v3d->dev, "Failed to create TFU scheduler: %d.", + ret); + drm_sched_fini(&v3d->queue[V3D_RENDER].sched); + drm_sched_fini(&v3d->queue[V3D_BIN].sched); + return ret; + } + return 0; } diff --git a/drivers/gpu/drm/v3d/v3d_trace.h b/drivers/gpu/drm/v3d/v3d_trace.h index 85dd351e1e09..edd984afa33f 100644 --- a/drivers/gpu/drm/v3d/v3d_trace.h +++ b/drivers/gpu/drm/v3d/v3d_trace.h @@ -12,6 +12,28 @@ #define TRACE_SYSTEM v3d #define TRACE_INCLUDE_FILE v3d_trace +TRACE_EVENT(v3d_submit_cl_ioctl, + TP_PROTO(struct drm_device *dev, u32 ct1qba, u32 ct1qea), + TP_ARGS(dev, ct1qba, ct1qea), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u32, ct1qba) + __field(u32, ct1qea) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->ct1qba = ct1qba; + __entry->ct1qea = ct1qea; + ), + + TP_printk("dev=%u, RCL 0x%08x..0x%08x", + __entry->dev, + __entry->ct1qba, + __entry->ct1qea) +); + TRACE_EVENT(v3d_submit_cl, TP_PROTO(struct drm_device *dev, bool is_render, uint64_t seqno, @@ -42,6 +64,105 @@ TRACE_EVENT(v3d_submit_cl, __entry->ctnqea) ); +TRACE_EVENT(v3d_bcl_irq, + TP_PROTO(struct drm_device *dev, + uint64_t seqno), + TP_ARGS(dev, seqno), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u64, seqno) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->seqno = seqno; + ), + + TP_printk("dev=%u, seqno=%llu", + __entry->dev, + __entry->seqno) +); + +TRACE_EVENT(v3d_rcl_irq, + TP_PROTO(struct drm_device *dev, + uint64_t seqno), + TP_ARGS(dev, seqno), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u64, seqno) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->seqno = seqno; + ), + + TP_printk("dev=%u, seqno=%llu", + __entry->dev, + __entry->seqno) +); + +TRACE_EVENT(v3d_tfu_irq, + TP_PROTO(struct drm_device *dev, + uint64_t seqno), + TP_ARGS(dev, seqno), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u64, seqno) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->seqno = seqno; + ), + + TP_printk("dev=%u, seqno=%llu", + __entry->dev, + __entry->seqno) +); + +TRACE_EVENT(v3d_submit_tfu_ioctl, + TP_PROTO(struct drm_device *dev, u32 iia), + TP_ARGS(dev, iia), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u32, iia) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->iia = iia; + ), + + TP_printk("dev=%u, IIA 0x%08x", + __entry->dev, + __entry->iia) +); + +TRACE_EVENT(v3d_submit_tfu, + TP_PROTO(struct drm_device *dev, + uint64_t seqno), + TP_ARGS(dev, seqno), + + TP_STRUCT__entry( + __field(u32, dev) + __field(u64, seqno) + ), + + TP_fast_assign( + __entry->dev = dev->primary->index; + __entry->seqno = seqno; + ), + + TP_printk("dev=%u, seqno=%llu", + __entry->dev, + __entry->seqno) +); + TRACE_EVENT(v3d_reset_begin, TP_PROTO(struct drm_device *dev), TP_ARGS(dev), diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 3ce136ba8791..97caf1671dd0 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -49,6 +49,13 @@ struct vc4_crtc_state { struct drm_mm_node mm; bool feed_txp; bool txp_armed; + + struct { + unsigned int left; + unsigned int right; + unsigned int top; + unsigned int bottom; + } margins; }; static inline struct vc4_crtc_state * @@ -624,6 +631,37 @@ static enum drm_mode_status vc4_crtc_mode_valid(struct drm_crtc *crtc, return MODE_OK; } +void vc4_crtc_get_margins(struct drm_crtc_state *state, + unsigned int *left, unsigned int *right, + unsigned int *top, unsigned int *bottom) +{ + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); + struct drm_connector_state *conn_state; + struct drm_connector *conn; + int i; + + *left = vc4_state->margins.left; + *right = vc4_state->margins.right; + *top = vc4_state->margins.top; + *bottom = vc4_state->margins.bottom; + + /* We have to interate over all new connector states because + * vc4_crtc_get_margins() might be called before + * vc4_crtc_atomic_check() which means margins info in vc4_crtc_state + * might be outdated. + */ + for_each_new_connector_in_state(state->state, conn, conn_state, i) { + if (conn_state->crtc != state->crtc) + continue; + + *left = conn_state->tv.margins.left; + *right = conn_state->tv.margins.right; + *top = conn_state->tv.margins.top; + *bottom = conn_state->tv.margins.bottom; + break; + } +} + static int vc4_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { @@ -671,6 +709,10 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc, vc4_state->feed_txp = false; } + vc4_state->margins.left = conn_state->tv.margins.left; + vc4_state->margins.right = conn_state->tv.margins.right; + vc4_state->margins.top = conn_state->tv.margins.top; + vc4_state->margins.bottom = conn_state->tv.margins.bottom; break; } @@ -972,6 +1014,7 @@ static struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc) old_vc4_state = to_vc4_crtc_state(crtc->state); vc4_state->feed_txp = old_vc4_state->feed_txp; + vc4_state->margins = old_vc4_state->margins; __drm_atomic_helper_crtc_duplicate_state(crtc, &vc4_state->base); return &vc4_state->base; diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index bd6ef1f31822..c24b078f0593 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -338,6 +338,7 @@ struct vc4_plane_state { u32 pos0_offset; u32 pos2_offset; u32 ptr0_offset; + u32 lbm_offset; /* Offset where the plane's dlist was last stored in the * hardware at vc4_crtc_atomic_flush() time. @@ -369,6 +370,11 @@ struct vc4_plane_state { * to enable background color fill. */ bool needs_bg_fill; + + /* Mark the dlist as initialized. Useful to avoid initializing it twice + * when async update is not possible. + */ + bool dlist_initialized; }; static inline struct vc4_plane_state * @@ -701,6 +707,9 @@ bool vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, const struct drm_display_mode *mode); void vc4_crtc_handle_vblank(struct vc4_crtc *crtc); void vc4_crtc_txp_armed(struct drm_crtc_state *state); +void vc4_crtc_get_margins(struct drm_crtc_state *state, + unsigned int *right, unsigned int *left, + unsigned int *top, unsigned int *bottom); /* vc4_debugfs.c */ int vc4_debugfs_init(struct drm_minor *minor); diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index 41881ce4132d..aea2b8dfec17 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -681,7 +681,7 @@ vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec, exec->fence = &fence->base; if (out_sync) - drm_syncobj_replace_fence(out_sync, 0, exec->fence); + drm_syncobj_replace_fence(out_sync, exec->fence); vc4_update_bo_seqnos(exec, seqno); diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index fd5522fd179e..2f276222e30f 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -310,6 +310,7 @@ static struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev, { struct drm_connector *connector; struct vc4_hdmi_connector *hdmi_connector; + int ret; hdmi_connector = devm_kzalloc(dev->dev, sizeof(*hdmi_connector), GFP_KERNEL); @@ -323,6 +324,13 @@ static struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev, DRM_MODE_CONNECTOR_HDMIA); drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs); + /* Create and attach TV margin props to this connector. */ + ret = drm_mode_create_tv_margin_properties(dev); + if (ret) + return ERR_PTR(ret); + + drm_connector_attach_tv_margin_properties(connector); + connector->polled = (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT); @@ -408,6 +416,9 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) { struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder); + struct vc4_dev *vc4 = encoder->dev->dev_private; + struct vc4_hdmi *hdmi = vc4->hdmi; + struct drm_connector_state *cstate = hdmi->connector->state; struct drm_crtc *crtc = encoder->crtc; const struct drm_display_mode *mode = &crtc->state->adjusted_mode; union hdmi_infoframe frame; @@ -426,6 +437,11 @@ static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder) vc4_encoder->rgb_range_selectable, false); + frame.avi.right_bar = cstate->tv.margins.right; + frame.avi.left_bar = cstate->tv.margins.left; + frame.avi.top_bar = cstate->tv.margins.top; + frame.avi.bottom_bar = cstate->tv.margins.bottom; + vc4_hdmi_write_infoframe(encoder, &frame); } diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 127468785f74..0490edb192a1 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -214,6 +214,12 @@ static int vc4_atomic_commit(struct drm_device *dev, return 0; } + /* We know for sure we don't want an async update here. Set + * state->legacy_cursor_update to false to prevent + * drm_atomic_helper_setup_commit() from auto-completing + * commit->flip_done. + */ + state->legacy_cursor_update = false; ret = drm_atomic_helper_setup_commit(state, nonblock); if (ret) return ret; @@ -426,7 +432,8 @@ int vc4_kms_load(struct drm_device *dev) ctm_state = kzalloc(sizeof(*ctm_state), GFP_KERNEL); if (!ctm_state) return -ENOMEM; - drm_atomic_private_obj_init(&vc4->ctm_manager, &ctm_state->base, + + drm_atomic_private_obj_init(dev, &vc4->ctm_manager, &ctm_state->base, &vc4_ctm_state_funcs); drm_mode_config_reset(dev); diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 98fae4daa08c..2901ed0c5223 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -129,12 +129,12 @@ static const struct hvs_format *vc4_get_hvs_format(u32 drm_format) static enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst) { - if (dst > src) + if (dst == src) + return VC4_SCALING_NONE; + if (3 * dst >= 2 * src) return VC4_SCALING_PPF; - else if (dst < src) - return VC4_SCALING_TPZ; else - return VC4_SCALING_NONE; + return VC4_SCALING_TPZ; } static bool plane_enabled(struct drm_plane_state *state) @@ -154,6 +154,7 @@ static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane return NULL; memset(&vc4_state->lbm, 0, sizeof(vc4_state->lbm)); + vc4_state->dlist_initialized = 0; __drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base); @@ -257,16 +258,60 @@ static u32 vc4_get_scl_field(struct drm_plane_state *state, int plane) } } +static int vc4_plane_margins_adj(struct drm_plane_state *pstate) +{ + struct vc4_plane_state *vc4_pstate = to_vc4_plane_state(pstate); + unsigned int left, right, top, bottom, adjhdisplay, adjvdisplay; + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_new_crtc_state(pstate->state, + pstate->crtc); + + vc4_crtc_get_margins(crtc_state, &left, &right, &top, &bottom); + if (!left && !right && !top && !bottom) + return 0; + + if (left + right >= crtc_state->mode.hdisplay || + top + bottom >= crtc_state->mode.vdisplay) + return -EINVAL; + + adjhdisplay = crtc_state->mode.hdisplay - (left + right); + vc4_pstate->crtc_x = DIV_ROUND_CLOSEST(vc4_pstate->crtc_x * + adjhdisplay, + crtc_state->mode.hdisplay); + vc4_pstate->crtc_x += left; + if (vc4_pstate->crtc_x > crtc_state->mode.hdisplay - left) + vc4_pstate->crtc_x = crtc_state->mode.hdisplay - left; + + adjvdisplay = crtc_state->mode.vdisplay - (top + bottom); + vc4_pstate->crtc_y = DIV_ROUND_CLOSEST(vc4_pstate->crtc_y * + adjvdisplay, + crtc_state->mode.vdisplay); + vc4_pstate->crtc_y += top; + if (vc4_pstate->crtc_y > crtc_state->mode.vdisplay - top) + vc4_pstate->crtc_y = crtc_state->mode.vdisplay - top; + + vc4_pstate->crtc_w = DIV_ROUND_CLOSEST(vc4_pstate->crtc_w * + adjhdisplay, + crtc_state->mode.hdisplay); + vc4_pstate->crtc_h = DIV_ROUND_CLOSEST(vc4_pstate->crtc_h * + adjvdisplay, + crtc_state->mode.vdisplay); + + if (!vc4_pstate->crtc_w || !vc4_pstate->crtc_h) + return -EINVAL; + + return 0; +} + static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) { - struct drm_plane *plane = state->plane; struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); struct drm_framebuffer *fb = state->fb; struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0); u32 subpixel_src_mask = (1 << 16) - 1; u32 format = fb->format->format; int num_planes = fb->format->num_planes; - int min_scale = 1, max_scale = INT_MAX; struct drm_crtc_state *crtc_state; u32 h_subsample, v_subsample; int i, ret; @@ -278,21 +323,8 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) return -EINVAL; } - /* No configuring scaling on the cursor plane, since it gets - * non-vblank-synced updates, and scaling requires LBM changes which - * have to be vblank-synced. - */ - if (plane->type == DRM_PLANE_TYPE_CURSOR) { - min_scale = DRM_PLANE_HELPER_NO_SCALING; - max_scale = DRM_PLANE_HELPER_NO_SCALING; - } else { - min_scale = 1; - max_scale = INT_MAX; - } - - ret = drm_atomic_helper_check_plane_state(state, crtc_state, - min_scale, max_scale, - true, true); + ret = drm_atomic_helper_check_plane_state(state, crtc_state, 1, + INT_MAX, true, true); if (ret) return ret; @@ -320,6 +352,10 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) vc4_state->crtc_w = state->dst.x2 - state->dst.x1; vc4_state->crtc_h = state->dst.y2 - state->dst.y1; + ret = vc4_plane_margins_adj(state); + if (ret) + return ret; + vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0], vc4_state->crtc_w); vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0], @@ -341,12 +377,14 @@ static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state) vc4_get_scaling_mode(vc4_state->src_h[1], vc4_state->crtc_h); - /* YUV conversion requires that horizontal scaling be enabled, - * even on a plane that's otherwise 1:1. Looks like only PPF - * works in that case, so let's pick that one. + /* YUV conversion requires that horizontal scaling be enabled + * on the UV plane even if vc4_get_scaling_mode() returned + * VC4_SCALING_NONE (which can happen when the down-scaling + * ratio is 0.5). Let's force it to VC4_SCALING_PPF in this + * case. */ - if (vc4_state->is_unity) - vc4_state->x_scaling[0] = VC4_SCALING_PPF; + if (vc4_state->x_scaling[1] == VC4_SCALING_NONE) + vc4_state->x_scaling[1] = VC4_SCALING_PPF; } else { vc4_state->is_yuv = false; vc4_state->x_scaling[1] = VC4_SCALING_NONE; @@ -393,10 +431,13 @@ static u32 vc4_lbm_size(struct drm_plane_state *state) u32 pix_per_line = max(vc4_state->src_w[0], (u32)vc4_state->crtc_w); u32 lbm; + /* LBM is not needed when there's no vertical scaling. */ + if (vc4_state->y_scaling[0] == VC4_SCALING_NONE && + vc4_state->y_scaling[1] == VC4_SCALING_NONE) + return 0; + if (!vc4_state->is_yuv) { - if (vc4_state->is_unity) - return 0; - else if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ) + if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ) lbm = pix_per_line * 8; else { /* In special cases, this multiplier might be 12. */ @@ -447,6 +488,43 @@ static void vc4_write_scaling_parameters(struct drm_plane_state *state, } } +static int vc4_plane_allocate_lbm(struct drm_plane_state *state) +{ + struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev); + struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + unsigned long irqflags; + u32 lbm_size; + + lbm_size = vc4_lbm_size(state); + if (!lbm_size) + return 0; + + if (WARN_ON(!vc4_state->lbm_offset)) + return -EINVAL; + + /* Allocate the LBM memory that the HVS will use for temporary + * storage due to our scaling/format conversion. + */ + if (!vc4_state->lbm.allocated) { + int ret; + + spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); + ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm, + &vc4_state->lbm, + lbm_size, 32, 0, 0); + spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); + + if (ret) + return ret; + } else { + WARN_ON_ONCE(lbm_size != vc4_state->lbm.size); + } + + vc4_state->dlist[vc4_state->lbm_offset] = vc4_state->lbm.start; + + return 0; +} + /* Writes out a full display list for an active plane to the plane's * private dlist state. */ @@ -464,31 +542,15 @@ static int vc4_plane_mode_set(struct drm_plane *plane, bool mix_plane_alpha; bool covers_screen; u32 scl0, scl1, pitch0; - u32 lbm_size, tiling; - unsigned long irqflags; + u32 tiling, src_y; u32 hvs_format = format->hvs; + unsigned int rotation; int ret, i; - ret = vc4_plane_setup_clipping_and_scaling(state); - if (ret) - return ret; - - /* Allocate the LBM memory that the HVS will use for temporary - * storage due to our scaling/format conversion. - */ - lbm_size = vc4_lbm_size(state); - if (lbm_size) { - if (!vc4_state->lbm.allocated) { - spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags); - ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm, - &vc4_state->lbm, - lbm_size, 32, 0, 0); - spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags); - } else { - WARN_ON_ONCE(lbm_size != vc4_state->lbm.size); - } - } + if (vc4_state->dlist_initialized) + return 0; + ret = vc4_plane_setup_clipping_and_scaling(state); if (ret) return ret; @@ -509,6 +571,16 @@ static int vc4_plane_mode_set(struct drm_plane *plane, h_subsample = drm_format_horz_chroma_subsampling(format->drm); v_subsample = drm_format_vert_chroma_subsampling(format->drm); + rotation = drm_rotation_simplify(state->rotation, + DRM_MODE_ROTATE_0 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y); + + /* We must point to the last line when Y reflection is enabled. */ + src_y = vc4_state->src_y; + if (rotation & DRM_MODE_REFLECT_Y) + src_y += vc4_state->src_h[0] - 1; + switch (base_format_mod) { case DRM_FORMAT_MOD_LINEAR: tiling = SCALER_CTL0_TILING_LINEAR; @@ -518,9 +590,10 @@ static int vc4_plane_mode_set(struct drm_plane *plane, * out. */ for (i = 0; i < num_planes; i++) { - vc4_state->offsets[i] += vc4_state->src_y / + vc4_state->offsets[i] += src_y / (i ? v_subsample : 1) * fb->pitches[i]; + vc4_state->offsets[i] += vc4_state->src_x / (i ? h_subsample : 1) * fb->format->cpp[i]; @@ -546,22 +619,38 @@ static int vc4_plane_mode_set(struct drm_plane *plane, u32 tiles_w = fb->pitches[0] >> (tile_size_shift - tile_h_shift); u32 tiles_l = vc4_state->src_x >> tile_w_shift; u32 tiles_r = tiles_w - tiles_l; - u32 tiles_t = vc4_state->src_y >> tile_h_shift; + u32 tiles_t = src_y >> tile_h_shift; /* Intra-tile offsets, which modify the base address (the * SCALER_PITCH0_TILE_Y_OFFSET tells HVS how to walk from that * base address). */ - u32 tile_y = (vc4_state->src_y >> 4) & 1; - u32 subtile_y = (vc4_state->src_y >> 2) & 3; - u32 utile_y = vc4_state->src_y & 3; + u32 tile_y = (src_y >> 4) & 1; + u32 subtile_y = (src_y >> 2) & 3; + u32 utile_y = src_y & 3; u32 x_off = vc4_state->src_x & tile_w_mask; - u32 y_off = vc4_state->src_y & tile_h_mask; + u32 y_off = src_y & tile_h_mask; + + /* When Y reflection is requested we must set the + * SCALER_PITCH0_TILE_LINE_DIR flag to tell HVS that all lines + * after the initial one should be fetched in descending order, + * which makes sense since we start from the last line and go + * backward. + * Don't know why we need y_off = max_y_off - y_off, but it's + * definitely required (I guess it's also related to the "going + * backward" situation). + */ + if (rotation & DRM_MODE_REFLECT_Y) { + y_off = tile_h_mask - y_off; + pitch0 = SCALER_PITCH0_TILE_LINE_DIR; + } else { + pitch0 = 0; + } tiling = SCALER_CTL0_TILING_256B_OR_T; - pitch0 = (VC4_SET_FIELD(x_off, SCALER_PITCH0_SINK_PIX) | - VC4_SET_FIELD(y_off, SCALER_PITCH0_TILE_Y_OFFSET) | - VC4_SET_FIELD(tiles_l, SCALER_PITCH0_TILE_WIDTH_L) | - VC4_SET_FIELD(tiles_r, SCALER_PITCH0_TILE_WIDTH_R)); + pitch0 |= (VC4_SET_FIELD(x_off, SCALER_PITCH0_SINK_PIX) | + VC4_SET_FIELD(y_off, SCALER_PITCH0_TILE_Y_OFFSET) | + VC4_SET_FIELD(tiles_l, SCALER_PITCH0_TILE_WIDTH_L) | + VC4_SET_FIELD(tiles_r, SCALER_PITCH0_TILE_WIDTH_R)); vc4_state->offsets[0] += tiles_t * (tiles_w << tile_size_shift); vc4_state->offsets[0] += subtile_y << 8; vc4_state->offsets[0] += utile_y << 4; @@ -584,6 +673,7 @@ static int vc4_plane_mode_set(struct drm_plane *plane, case DRM_FORMAT_MOD_BROADCOM_SAND128: case DRM_FORMAT_MOD_BROADCOM_SAND256: { uint32_t param = fourcc_mod_broadcom_param(fb->modifier); + u32 tile_w, tile, x_off, pix_per_tile; /* Column-based NV12 or RGBA. */ @@ -603,12 +693,15 @@ static int vc4_plane_mode_set(struct drm_plane *plane, switch (base_format_mod) { case DRM_FORMAT_MOD_BROADCOM_SAND64: tiling = SCALER_CTL0_TILING_64B; + tile_w = 64; break; case DRM_FORMAT_MOD_BROADCOM_SAND128: tiling = SCALER_CTL0_TILING_128B; + tile_w = 128; break; case DRM_FORMAT_MOD_BROADCOM_SAND256: tiling = SCALER_CTL0_TILING_256B_OR_T; + tile_w = 256; break; default: break; @@ -619,6 +712,23 @@ static int vc4_plane_mode_set(struct drm_plane *plane, return -EINVAL; } + pix_per_tile = tile_w / fb->format->cpp[0]; + tile = vc4_state->src_x / pix_per_tile; + x_off = vc4_state->src_x % pix_per_tile; + + /* Adjust the base pointer to the first pixel to be scanned + * out. + */ + for (i = 0; i < num_planes; i++) { + vc4_state->offsets[i] += param * tile_w * tile; + vc4_state->offsets[i] += src_y / + (i ? v_subsample : 1) * + tile_w; + vc4_state->offsets[i] += x_off / + (i ? h_subsample : 1) * + fb->format->cpp[i]; + } + pitch0 = VC4_SET_FIELD(param, SCALER_TILE_HEIGHT); break; } @@ -632,6 +742,8 @@ static int vc4_plane_mode_set(struct drm_plane *plane, /* Control word */ vc4_dlist_write(vc4_state, SCALER_CTL0_VALID | + (rotation & DRM_MODE_REFLECT_X ? SCALER_CTL0_HFLIP : 0) | + (rotation & DRM_MODE_REFLECT_Y ? SCALER_CTL0_VFLIP : 0) | VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND, SCALER_CTL0_RGBA_EXPAND) | (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) | (hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) | @@ -712,15 +824,18 @@ static int vc4_plane_mode_set(struct drm_plane *plane, vc4_dlist_write(vc4_state, SCALER_CSC2_ITR_R_601_5); } + vc4_state->lbm_offset = 0; + if (vc4_state->x_scaling[0] != VC4_SCALING_NONE || vc4_state->x_scaling[1] != VC4_SCALING_NONE || vc4_state->y_scaling[0] != VC4_SCALING_NONE || vc4_state->y_scaling[1] != VC4_SCALING_NONE) { - /* LBM Base Address. */ + /* Reserve a slot for the LBM Base Address. The real value will + * be set when calling vc4_plane_allocate_lbm(). + */ if (vc4_state->y_scaling[0] != VC4_SCALING_NONE || - vc4_state->y_scaling[1] != VC4_SCALING_NONE) { - vc4_dlist_write(vc4_state, vc4_state->lbm.start); - } + vc4_state->y_scaling[1] != VC4_SCALING_NONE) + vc4_state->lbm_offset = vc4_state->dlist_count++; if (num_planes > 1) { /* Emit Cb/Cr as channel 0 and Y as channel @@ -766,6 +881,13 @@ static int vc4_plane_mode_set(struct drm_plane *plane, vc4_state->needs_bg_fill = fb->format->has_alpha || !covers_screen || state->alpha != DRM_BLEND_ALPHA_OPAQUE; + /* Flag the dlist as initialized to avoid checking it twice in case + * the async update check already called vc4_plane_mode_set() and + * decided to fallback to sync update because async update was not + * possible. + */ + vc4_state->dlist_initialized = 1; + return 0; } @@ -780,13 +902,18 @@ static int vc4_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { struct vc4_plane_state *vc4_state = to_vc4_plane_state(state); + int ret; vc4_state->dlist_count = 0; - if (plane_enabled(state)) - return vc4_plane_mode_set(plane, state); - else + if (!plane_enabled(state)) return 0; + + ret = vc4_plane_mode_set(plane, state); + if (ret) + return ret; + + return vc4_plane_allocate_lbm(state); } static void vc4_plane_atomic_update(struct drm_plane *plane, @@ -852,28 +979,59 @@ void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb) static void vc4_plane_atomic_async_update(struct drm_plane *plane, struct drm_plane_state *state) { - struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); + struct vc4_plane_state *vc4_state, *new_vc4_state; - if (plane->state->fb != state->fb) { - vc4_plane_async_set_fb(plane, state->fb); - drm_atomic_set_fb_for_plane(plane->state, state->fb); - } - - /* Set the cursor's position on the screen. This is the - * expected change from the drm_mode_cursor_universal() - * helper. - */ + drm_atomic_set_fb_for_plane(plane->state, state->fb); plane->state->crtc_x = state->crtc_x; plane->state->crtc_y = state->crtc_y; - - /* Allow changing the start position within the cursor BO, if - * that matters. - */ + plane->state->crtc_w = state->crtc_w; + plane->state->crtc_h = state->crtc_h; plane->state->src_x = state->src_x; plane->state->src_y = state->src_y; - - /* Update the display list based on the new crtc_x/y. */ - vc4_plane_atomic_check(plane, plane->state); + plane->state->src_w = state->src_w; + plane->state->src_h = state->src_h; + plane->state->src_h = state->src_h; + plane->state->alpha = state->alpha; + plane->state->pixel_blend_mode = state->pixel_blend_mode; + plane->state->rotation = state->rotation; + plane->state->zpos = state->zpos; + plane->state->normalized_zpos = state->normalized_zpos; + plane->state->color_encoding = state->color_encoding; + plane->state->color_range = state->color_range; + plane->state->src = state->src; + plane->state->dst = state->dst; + plane->state->visible = state->visible; + + new_vc4_state = to_vc4_plane_state(state); + vc4_state = to_vc4_plane_state(plane->state); + + vc4_state->crtc_x = new_vc4_state->crtc_x; + vc4_state->crtc_y = new_vc4_state->crtc_y; + vc4_state->crtc_h = new_vc4_state->crtc_h; + vc4_state->crtc_w = new_vc4_state->crtc_w; + vc4_state->src_x = new_vc4_state->src_x; + vc4_state->src_y = new_vc4_state->src_y; + memcpy(vc4_state->src_w, new_vc4_state->src_w, + sizeof(vc4_state->src_w)); + memcpy(vc4_state->src_h, new_vc4_state->src_h, + sizeof(vc4_state->src_h)); + memcpy(vc4_state->x_scaling, new_vc4_state->x_scaling, + sizeof(vc4_state->x_scaling)); + memcpy(vc4_state->y_scaling, new_vc4_state->y_scaling, + sizeof(vc4_state->y_scaling)); + vc4_state->is_unity = new_vc4_state->is_unity; + vc4_state->is_yuv = new_vc4_state->is_yuv; + memcpy(vc4_state->offsets, new_vc4_state->offsets, + sizeof(vc4_state->offsets)); + vc4_state->needs_bg_fill = new_vc4_state->needs_bg_fill; + + /* Update the current vc4_state pos0, pos2 and ptr0 dlist entries. */ + vc4_state->dlist[vc4_state->pos0_offset] = + new_vc4_state->dlist[vc4_state->pos0_offset]; + vc4_state->dlist[vc4_state->pos2_offset] = + new_vc4_state->dlist[vc4_state->pos2_offset]; + vc4_state->dlist[vc4_state->ptr0_offset] = + new_vc4_state->dlist[vc4_state->ptr0_offset]; /* Note that we can't just call vc4_plane_write_dlist() * because that would smash the context data that the HVS is @@ -890,13 +1048,38 @@ static void vc4_plane_atomic_async_update(struct drm_plane *plane, static int vc4_plane_atomic_async_check(struct drm_plane *plane, struct drm_plane_state *state) { - /* No configuring new scaling in the fast path. */ - if (plane->state->crtc_w != state->crtc_w || - plane->state->crtc_h != state->crtc_h || - plane->state->src_w != state->src_w || - plane->state->src_h != state->src_h) + struct vc4_plane_state *old_vc4_state, *new_vc4_state; + int ret; + u32 i; + + ret = vc4_plane_mode_set(plane, state); + if (ret) + return ret; + + old_vc4_state = to_vc4_plane_state(plane->state); + new_vc4_state = to_vc4_plane_state(state); + if (old_vc4_state->dlist_count != new_vc4_state->dlist_count || + old_vc4_state->pos0_offset != new_vc4_state->pos0_offset || + old_vc4_state->pos2_offset != new_vc4_state->pos2_offset || + old_vc4_state->ptr0_offset != new_vc4_state->ptr0_offset || + vc4_lbm_size(plane->state) != vc4_lbm_size(state)) return -EINVAL; + /* Only pos0, pos2 and ptr0 DWORDS can be updated in an async update + * if anything else has changed, fallback to a sync update. + */ + for (i = 0; i < new_vc4_state->dlist_count; i++) { + if (i == new_vc4_state->pos0_offset || + i == new_vc4_state->pos2_offset || + i == new_vc4_state->ptr0_offset || + (new_vc4_state->lbm_offset && + i == new_vc4_state->lbm_offset)) + continue; + + if (new_vc4_state->dlist[i] != old_vc4_state->dlist[i]) + return -EINVAL; + } + return 0; } @@ -1013,7 +1196,6 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, struct drm_plane *plane = NULL; struct vc4_plane *vc4_plane; u32 formats[ARRAY_SIZE(hvs_formats)]; - u32 num_formats = 0; int ret = 0; unsigned i; static const uint64_t modifiers[] = { @@ -1030,25 +1212,23 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev, if (!vc4_plane) return ERR_PTR(-ENOMEM); - for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) { - /* Don't allow YUV in cursor planes, since that means - * tuning on the scaler, which we don't allow for the - * cursor. - */ - if (type != DRM_PLANE_TYPE_CURSOR || - hvs_formats[i].hvs < HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE) { - formats[num_formats++] = hvs_formats[i].drm; - } - } + for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) + formats[i] = hvs_formats[i].drm; + plane = &vc4_plane->base; ret = drm_universal_plane_init(dev, plane, 0, &vc4_plane_funcs, - formats, num_formats, + formats, ARRAY_SIZE(formats), modifiers, type, NULL); drm_plane_helper_add(plane, &vc4_plane_helper_funcs); drm_plane_create_alpha_property(plane); + drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0, + DRM_MODE_ROTATE_0 | + DRM_MODE_ROTATE_180 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y); return plane; } diff --git a/drivers/gpu/drm/vgem/vgem_fence.c b/drivers/gpu/drm/vgem/vgem_fence.c index c1c420afe2dd..eb17c0cd3727 100644 --- a/drivers/gpu/drm/vgem/vgem_fence.c +++ b/drivers/gpu/drm/vgem/vgem_fence.c @@ -53,13 +53,13 @@ static void vgem_fence_release(struct dma_fence *base) static void vgem_fence_value_str(struct dma_fence *fence, char *str, int size) { - snprintf(str, size, "%u", fence->seqno); + snprintf(str, size, "%llu", fence->seqno); } static void vgem_fence_timeline_value_str(struct dma_fence *fence, char *str, int size) { - snprintf(str, size, "%u", + snprintf(str, size, "%llu", dma_fence_is_signaled(fence) ? fence->seqno : 0); } diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c index 8f8fed471e34..e1c223e18d86 100644 --- a/drivers/gpu/drm/virtio/virtgpu_display.c +++ b/drivers/gpu/drm/virtio/virtgpu_display.c @@ -169,6 +169,12 @@ static int virtio_gpu_conn_get_modes(struct drm_connector *connector) struct drm_display_mode *mode = NULL; int count, width, height; + if (output->edid) { + count = drm_add_edid_modes(connector, output->edid); + if (count) + return count; + } + width = le32_to_cpu(output->info.r.width); height = le32_to_cpu(output->info.r.height); count = drm_add_modes_noedid(connector, XRES_MAX, YRES_MAX); @@ -287,6 +293,8 @@ static int vgdev_output_init(struct virtio_gpu_device *vgdev, int index) drm_connector_init(dev, connector, &virtio_gpu_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); drm_connector_helper_add(connector, &virtio_gpu_conn_helper_funcs); + if (vgdev->has_edid) + drm_connector_attach_edid_property(connector); drm_encoder_init(dev, encoder, &virtio_gpu_enc_funcs, DRM_MODE_ENCODER_VIRTUAL, NULL); @@ -378,6 +386,9 @@ int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev) void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev) { - virtio_gpu_fbdev_fini(vgdev); + int i; + + for (i = 0 ; i < vgdev->num_scanouts; ++i) + kfree(vgdev->outputs[i].edid); drm_mode_config_cleanup(vgdev->ddev); } diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index d9287c144fe5..7df50920c1e0 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -42,13 +42,20 @@ module_param_named(modeset, virtio_gpu_modeset, int, 0400); static int virtio_gpu_probe(struct virtio_device *vdev) { + int ret; + if (vgacon_text_force() && virtio_gpu_modeset == -1) return -EINVAL; if (virtio_gpu_modeset == 0) return -EINVAL; - return drm_virtio_init(&driver, vdev); + ret = drm_virtio_init(&driver, vdev); + if (ret) + return ret; + + drm_fbdev_generic_setup(vdev->priv, 32); + return 0; } static void virtio_gpu_remove(struct virtio_device *vdev) @@ -80,6 +87,7 @@ static unsigned int features[] = { */ VIRTIO_GPU_F_VIRGL, #endif + VIRTIO_GPU_F_EDID, }; static struct virtio_driver virtio_gpu_driver = { .feature_table = features, diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 2a8aaea72af3..63704915f8ce 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -47,8 +47,8 @@ #define DRIVER_DATE "0" #define DRIVER_MAJOR 0 -#define DRIVER_MINOR 0 -#define DRIVER_PATCHLEVEL 1 +#define DRIVER_MINOR 1 +#define DRIVER_PATCHLEVEL 0 /* virtgpu_drm_bus.c */ int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev); @@ -115,6 +115,7 @@ struct virtio_gpu_output { struct drm_encoder enc; struct virtio_gpu_display_one info; struct virtio_gpu_update_cursor cursor; + struct edid *edid; int cur_x; int cur_y; bool enabled; @@ -131,23 +132,15 @@ struct virtio_gpu_framebuffer { int x1, y1, x2, y2; /* dirty rect */ spinlock_t dirty_lock; uint32_t hw_res_handle; + struct virtio_gpu_fence *fence; }; #define to_virtio_gpu_framebuffer(x) \ container_of(x, struct virtio_gpu_framebuffer, base) -struct virtio_gpu_fbdev { - struct drm_fb_helper helper; - struct virtio_gpu_framebuffer vgfb; - struct virtio_gpu_device *vgdev; - struct delayed_work work; -}; - struct virtio_gpu_mman { struct ttm_bo_device bdev; }; -struct virtio_gpu_fbdev; - struct virtio_gpu_queue { struct virtqueue *vq; spinlock_t qlock; @@ -178,8 +171,6 @@ struct virtio_gpu_device { struct virtio_gpu_mman mman; - /* pointer to fbdev info structure */ - struct virtio_gpu_fbdev *vgfbdev; struct virtio_gpu_output outputs[VIRTIO_GPU_MAX_SCANOUTS]; uint32_t num_scanouts; @@ -200,6 +191,7 @@ struct virtio_gpu_device { struct ida ctx_id_ida; bool has_virgl_3d; + bool has_edid; struct work_struct config_changed_work; @@ -246,9 +238,6 @@ int virtio_gpu_mode_dumb_mmap(struct drm_file *file_priv, uint32_t handle, uint64_t *offset_p); /* virtio_fb */ -#define VIRTIO_GPUFB_CONN_LIMIT 1 -int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev); -void virtio_gpu_fbdev_fini(struct virtio_gpu_device *vgdev); int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *qfb, struct drm_clip_rect *clips, unsigned int num_clips); @@ -267,7 +256,7 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev, uint64_t offset, __le32 width, __le32 height, __le32 x, __le32 y, - struct virtio_gpu_fence **fence); + struct virtio_gpu_fence *fence); void virtio_gpu_cmd_resource_flush(struct virtio_gpu_device *vgdev, uint32_t resource_id, uint32_t x, uint32_t y, @@ -278,7 +267,7 @@ void virtio_gpu_cmd_set_scanout(struct virtio_gpu_device *vgdev, uint32_t x, uint32_t y); int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *obj, - struct virtio_gpu_fence **fence); + struct virtio_gpu_fence *fence); void virtio_gpu_object_detach(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *obj); int virtio_gpu_attach_status_page(struct virtio_gpu_device *vgdev); @@ -290,6 +279,7 @@ int virtio_gpu_cmd_get_capset_info(struct virtio_gpu_device *vgdev, int idx); int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev, int idx, int version, struct virtio_gpu_drv_cap_cache **cache_p); +int virtio_gpu_cmd_get_edids(struct virtio_gpu_device *vgdev); void virtio_gpu_cmd_context_create(struct virtio_gpu_device *vgdev, uint32_t id, uint32_t nlen, const char *name); void virtio_gpu_cmd_context_destroy(struct virtio_gpu_device *vgdev, @@ -302,23 +292,22 @@ void virtio_gpu_cmd_context_detach_resource(struct virtio_gpu_device *vgdev, uint32_t resource_id); void virtio_gpu_cmd_submit(struct virtio_gpu_device *vgdev, void *data, uint32_t data_size, - uint32_t ctx_id, struct virtio_gpu_fence **fence); + uint32_t ctx_id, struct virtio_gpu_fence *fence); void virtio_gpu_cmd_transfer_from_host_3d(struct virtio_gpu_device *vgdev, uint32_t resource_id, uint32_t ctx_id, uint64_t offset, uint32_t level, struct virtio_gpu_box *box, - struct virtio_gpu_fence **fence); + struct virtio_gpu_fence *fence); void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *bo, uint32_t ctx_id, uint64_t offset, uint32_t level, struct virtio_gpu_box *box, - struct virtio_gpu_fence **fence); + struct virtio_gpu_fence *fence); void virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *bo, - struct virtio_gpu_resource_create_3d *rc_3d, - struct virtio_gpu_fence **fence); + struct virtio_gpu_resource_create_3d *rc_3d); void virtio_gpu_ctrl_ack(struct virtqueue *vq); void virtio_gpu_cursor_ack(struct virtqueue *vq); void virtio_gpu_fence_ack(struct virtqueue *vq); @@ -346,9 +335,12 @@ void virtio_gpu_ttm_fini(struct virtio_gpu_device *vgdev); int virtio_gpu_mmap(struct file *filp, struct vm_area_struct *vma); /* virtio_gpu_fence.c */ +struct virtio_gpu_fence *virtio_gpu_fence_alloc( + struct virtio_gpu_device *vgdev); +void virtio_gpu_fence_cleanup(struct virtio_gpu_fence *fence); int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev, struct virtio_gpu_ctrl_hdr *cmd_hdr, - struct virtio_gpu_fence **fence); + struct virtio_gpu_fence *fence); void virtio_gpu_fence_event_process(struct virtio_gpu_device *vdev, u64 last_seq); diff --git a/drivers/gpu/drm/virtio/virtgpu_fb.c b/drivers/gpu/drm/virtio/virtgpu_fb.c index fb1cc8b2f119..b07584b1c2bf 100644 --- a/drivers/gpu/drm/virtio/virtgpu_fb.c +++ b/drivers/gpu/drm/virtio/virtgpu_fb.c @@ -27,8 +27,6 @@ #include <drm/drm_fb_helper.h> #include "virtgpu_drv.h" -#define VIRTIO_GPU_FBCON_POLL_PERIOD (HZ / 60) - static int virtio_gpu_dirty_update(struct virtio_gpu_framebuffer *fb, bool store, int x, int y, int width, int height) @@ -150,192 +148,3 @@ int virtio_gpu_surface_dirty(struct virtio_gpu_framebuffer *vgfb, left, top, right - left, bottom - top); return 0; } - -static void virtio_gpu_fb_dirty_work(struct work_struct *work) -{ - struct delayed_work *delayed_work = to_delayed_work(work); - struct virtio_gpu_fbdev *vfbdev = - container_of(delayed_work, struct virtio_gpu_fbdev, work); - struct virtio_gpu_framebuffer *vgfb = &vfbdev->vgfb; - - virtio_gpu_dirty_update(&vfbdev->vgfb, false, vgfb->x1, vgfb->y1, - vgfb->x2 - vgfb->x1, vgfb->y2 - vgfb->y1); -} - -static void virtio_gpu_3d_fillrect(struct fb_info *info, - const struct fb_fillrect *rect) -{ - struct virtio_gpu_fbdev *vfbdev = info->par; - - drm_fb_helper_sys_fillrect(info, rect); - virtio_gpu_dirty_update(&vfbdev->vgfb, true, rect->dx, rect->dy, - rect->width, rect->height); - schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD); -} - -static void virtio_gpu_3d_copyarea(struct fb_info *info, - const struct fb_copyarea *area) -{ - struct virtio_gpu_fbdev *vfbdev = info->par; - - drm_fb_helper_sys_copyarea(info, area); - virtio_gpu_dirty_update(&vfbdev->vgfb, true, area->dx, area->dy, - area->width, area->height); - schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD); -} - -static void virtio_gpu_3d_imageblit(struct fb_info *info, - const struct fb_image *image) -{ - struct virtio_gpu_fbdev *vfbdev = info->par; - - drm_fb_helper_sys_imageblit(info, image); - virtio_gpu_dirty_update(&vfbdev->vgfb, true, image->dx, image->dy, - image->width, image->height); - schedule_delayed_work(&vfbdev->work, VIRTIO_GPU_FBCON_POLL_PERIOD); -} - -static struct fb_ops virtio_gpufb_ops = { - .owner = THIS_MODULE, - DRM_FB_HELPER_DEFAULT_OPS, - .fb_fillrect = virtio_gpu_3d_fillrect, - .fb_copyarea = virtio_gpu_3d_copyarea, - .fb_imageblit = virtio_gpu_3d_imageblit, -}; - -static int virtio_gpufb_create(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes) -{ - struct virtio_gpu_fbdev *vfbdev = - container_of(helper, struct virtio_gpu_fbdev, helper); - struct drm_device *dev = helper->dev; - struct virtio_gpu_device *vgdev = dev->dev_private; - struct fb_info *info; - struct drm_framebuffer *fb; - struct drm_mode_fb_cmd2 mode_cmd = {}; - struct virtio_gpu_object *obj; - uint32_t format, size; - int ret; - - mode_cmd.width = sizes->surface_width; - mode_cmd.height = sizes->surface_height; - mode_cmd.pitches[0] = mode_cmd.width * 4; - mode_cmd.pixel_format = DRM_FORMAT_HOST_XRGB8888; - - format = virtio_gpu_translate_format(mode_cmd.pixel_format); - if (format == 0) - return -EINVAL; - - size = mode_cmd.pitches[0] * mode_cmd.height; - obj = virtio_gpu_alloc_object(dev, size, false, true); - if (IS_ERR(obj)) - return PTR_ERR(obj); - - virtio_gpu_cmd_create_resource(vgdev, obj, format, - mode_cmd.width, mode_cmd.height); - - ret = virtio_gpu_object_kmap(obj); - if (ret) { - DRM_ERROR("failed to kmap fb %d\n", ret); - goto err_obj_vmap; - } - - /* attach the object to the resource */ - ret = virtio_gpu_object_attach(vgdev, obj, NULL); - if (ret) - goto err_obj_attach; - - info = drm_fb_helper_alloc_fbi(helper); - if (IS_ERR(info)) { - ret = PTR_ERR(info); - goto err_fb_alloc; - } - - info->par = helper; - - ret = virtio_gpu_framebuffer_init(dev, &vfbdev->vgfb, - &mode_cmd, &obj->gem_base); - if (ret) - goto err_fb_alloc; - - fb = &vfbdev->vgfb.base; - - vfbdev->helper.fb = fb; - - strcpy(info->fix.id, "virtiodrmfb"); - info->fbops = &virtio_gpufb_ops; - info->pixmap.flags = FB_PIXMAP_SYSTEM; - - info->screen_buffer = obj->vmap; - info->screen_size = obj->gem_base.size; - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); - drm_fb_helper_fill_var(info, &vfbdev->helper, - sizes->fb_width, sizes->fb_height); - - info->fix.mmio_start = 0; - info->fix.mmio_len = 0; - return 0; - -err_fb_alloc: - virtio_gpu_object_detach(vgdev, obj); -err_obj_attach: -err_obj_vmap: - virtio_gpu_gem_free_object(&obj->gem_base); - return ret; -} - -static int virtio_gpu_fbdev_destroy(struct drm_device *dev, - struct virtio_gpu_fbdev *vgfbdev) -{ - struct virtio_gpu_framebuffer *vgfb = &vgfbdev->vgfb; - - drm_fb_helper_unregister_fbi(&vgfbdev->helper); - - if (vgfb->base.obj[0]) - vgfb->base.obj[0] = NULL; - drm_fb_helper_fini(&vgfbdev->helper); - drm_framebuffer_cleanup(&vgfb->base); - - return 0; -} -static const struct drm_fb_helper_funcs virtio_gpu_fb_helper_funcs = { - .fb_probe = virtio_gpufb_create, -}; - -int virtio_gpu_fbdev_init(struct virtio_gpu_device *vgdev) -{ - struct virtio_gpu_fbdev *vgfbdev; - int bpp_sel = 32; /* TODO: parameter from somewhere? */ - int ret; - - vgfbdev = kzalloc(sizeof(struct virtio_gpu_fbdev), GFP_KERNEL); - if (!vgfbdev) - return -ENOMEM; - - vgfbdev->vgdev = vgdev; - vgdev->vgfbdev = vgfbdev; - INIT_DELAYED_WORK(&vgfbdev->work, virtio_gpu_fb_dirty_work); - - drm_fb_helper_prepare(vgdev->ddev, &vgfbdev->helper, - &virtio_gpu_fb_helper_funcs); - ret = drm_fb_helper_init(vgdev->ddev, &vgfbdev->helper, - VIRTIO_GPUFB_CONN_LIMIT); - if (ret) { - kfree(vgfbdev); - return ret; - } - - drm_fb_helper_single_add_all_connectors(&vgfbdev->helper); - drm_fb_helper_initial_config(&vgfbdev->helper, bpp_sel); - return 0; -} - -void virtio_gpu_fbdev_fini(struct virtio_gpu_device *vgdev) -{ - if (!vgdev->vgfbdev) - return; - - virtio_gpu_fbdev_destroy(vgdev->ddev, vgdev->vgfbdev); - kfree(vgdev->vgfbdev); - vgdev->vgfbdev = NULL; -} diff --git a/drivers/gpu/drm/virtio/virtgpu_fence.c b/drivers/gpu/drm/virtio/virtgpu_fence.c index 00c742a441bf..4d6826b27814 100644 --- a/drivers/gpu/drm/virtio/virtgpu_fence.c +++ b/drivers/gpu/drm/virtio/virtgpu_fence.c @@ -67,28 +67,43 @@ static const struct dma_fence_ops virtio_fence_ops = { .timeline_value_str = virtio_timeline_value_str, }; +struct virtio_gpu_fence *virtio_gpu_fence_alloc(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv; + struct virtio_gpu_fence *fence = kzalloc(sizeof(struct virtio_gpu_fence), + GFP_ATOMIC); + if (!fence) + return fence; + + fence->drv = drv; + dma_fence_init(&fence->f, &virtio_fence_ops, &drv->lock, drv->context, 0); + + return fence; +} + +void virtio_gpu_fence_cleanup(struct virtio_gpu_fence *fence) +{ + if (!fence) + return; + + dma_fence_put(&fence->f); +} + int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev, struct virtio_gpu_ctrl_hdr *cmd_hdr, - struct virtio_gpu_fence **fence) + struct virtio_gpu_fence *fence) { struct virtio_gpu_fence_driver *drv = &vgdev->fence_drv; unsigned long irq_flags; - *fence = kmalloc(sizeof(struct virtio_gpu_fence), GFP_ATOMIC); - if ((*fence) == NULL) - return -ENOMEM; - spin_lock_irqsave(&drv->lock, irq_flags); - (*fence)->drv = drv; - (*fence)->seq = ++drv->sync_seq; - dma_fence_init(&(*fence)->f, &virtio_fence_ops, &drv->lock, - drv->context, (*fence)->seq); - dma_fence_get(&(*fence)->f); - list_add_tail(&(*fence)->node, &drv->fences); + fence->seq = ++drv->sync_seq; + dma_fence_get(&fence->f); + list_add_tail(&fence->node, &drv->fences); spin_unlock_irqrestore(&drv->lock, irq_flags); cmd_hdr->flags |= cpu_to_le32(VIRTIO_GPU_FLAG_FENCE); - cmd_hdr->fence_id = cpu_to_le64((*fence)->seq); + cmd_hdr->fence_id = cpu_to_le64(fence->seq); return 0; } diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c index bc5afa4f906e..161b80fee492 100644 --- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c +++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c @@ -28,6 +28,7 @@ #include <drm/drmP.h> #include <drm/virtgpu_drm.h> #include <drm/ttm/ttm_execbuf_util.h> +#include <linux/sync_file.h> #include "virtgpu_drv.h" @@ -105,7 +106,7 @@ static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data, struct virtio_gpu_device *vgdev = dev->dev_private; struct virtio_gpu_fpriv *vfpriv = drm_file->driver_priv; struct drm_gem_object *gobj; - struct virtio_gpu_fence *fence; + struct virtio_gpu_fence *out_fence; struct virtio_gpu_object *qobj; int ret; uint32_t *bo_handles = NULL; @@ -114,11 +115,46 @@ static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data, struct ttm_validate_buffer *buflist = NULL; int i; struct ww_acquire_ctx ticket; + struct sync_file *sync_file; + int in_fence_fd = exbuf->fence_fd; + int out_fence_fd = -1; void *buf; if (vgdev->has_virgl_3d == false) return -ENOSYS; + if ((exbuf->flags & ~VIRTGPU_EXECBUF_FLAGS)) + return -EINVAL; + + exbuf->fence_fd = -1; + + if (exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_IN) { + struct dma_fence *in_fence; + + in_fence = sync_file_get_fence(in_fence_fd); + + if (!in_fence) + return -EINVAL; + + /* + * Wait if the fence is from a foreign context, or if the fence + * array contains any fence from a foreign context. + */ + ret = 0; + if (!dma_fence_match_context(in_fence, vgdev->fence_drv.context)) + ret = dma_fence_wait(in_fence, true); + + dma_fence_put(in_fence); + if (ret) + return ret; + } + + if (exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) { + out_fence_fd = get_unused_fd_flags(O_CLOEXEC); + if (out_fence_fd < 0) + return out_fence_fd; + } + INIT_LIST_HEAD(&validate_list); if (exbuf->num_bo_handles) { @@ -128,26 +164,22 @@ static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data, sizeof(struct ttm_validate_buffer), GFP_KERNEL | __GFP_ZERO); if (!bo_handles || !buflist) { - kvfree(bo_handles); - kvfree(buflist); - return -ENOMEM; + ret = -ENOMEM; + goto out_unused_fd; } user_bo_handles = (void __user *)(uintptr_t)exbuf->bo_handles; if (copy_from_user(bo_handles, user_bo_handles, exbuf->num_bo_handles * sizeof(uint32_t))) { ret = -EFAULT; - kvfree(bo_handles); - kvfree(buflist); - return ret; + goto out_unused_fd; } for (i = 0; i < exbuf->num_bo_handles; i++) { gobj = drm_gem_object_lookup(drm_file, bo_handles[i]); if (!gobj) { - kvfree(bo_handles); - kvfree(buflist); - return -ENOENT; + ret = -ENOENT; + goto out_unused_fd; } qobj = gem_to_virtio_gpu_obj(gobj); @@ -156,6 +188,7 @@ static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data, list_add(&buflist[i].head, &validate_list); } kvfree(bo_handles); + bo_handles = NULL; } ret = virtio_gpu_object_list_validate(&ticket, &validate_list); @@ -168,22 +201,48 @@ static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data, ret = PTR_ERR(buf); goto out_unresv; } + + out_fence = virtio_gpu_fence_alloc(vgdev); + if(!out_fence) { + ret = -ENOMEM; + goto out_memdup; + } + + if (out_fence_fd >= 0) { + sync_file = sync_file_create(&out_fence->f); + if (!sync_file) { + dma_fence_put(&out_fence->f); + ret = -ENOMEM; + goto out_memdup; + } + + exbuf->fence_fd = out_fence_fd; + fd_install(out_fence_fd, sync_file->file); + } + virtio_gpu_cmd_submit(vgdev, buf, exbuf->size, - vfpriv->ctx_id, &fence); + vfpriv->ctx_id, out_fence); - ttm_eu_fence_buffer_objects(&ticket, &validate_list, &fence->f); + ttm_eu_fence_buffer_objects(&ticket, &validate_list, &out_fence->f); /* fence the command bo */ virtio_gpu_unref_list(&validate_list); kvfree(buflist); - dma_fence_put(&fence->f); return 0; +out_memdup: + kfree(buf); out_unresv: ttm_eu_backoff_reservation(&ticket, &validate_list); out_free: virtio_gpu_unref_list(&validate_list); +out_unused_fd: + kvfree(bo_handles); kvfree(buflist); + + if (out_fence_fd >= 0) + put_unused_fd(out_fence_fd); + return ret; } @@ -283,11 +342,17 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, rc_3d.nr_samples = cpu_to_le32(rc->nr_samples); rc_3d.flags = cpu_to_le32(rc->flags); - virtio_gpu_cmd_resource_create_3d(vgdev, qobj, &rc_3d, NULL); - ret = virtio_gpu_object_attach(vgdev, qobj, &fence); + fence = virtio_gpu_fence_alloc(vgdev); + if (!fence) { + ret = -ENOMEM; + goto fail_backoff; + } + + virtio_gpu_cmd_resource_create_3d(vgdev, qobj, &rc_3d); + ret = virtio_gpu_object_attach(vgdev, qobj, fence); if (ret) { - ttm_eu_backoff_reservation(&ticket, &validate_list); - goto fail_unref; + virtio_gpu_fence_cleanup(fence); + goto fail_backoff; } ttm_eu_fence_buffer_objects(&ticket, &validate_list, &fence->f); } @@ -312,6 +377,8 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data, dma_fence_put(&fence->f); } return 0; +fail_backoff: + ttm_eu_backoff_reservation(&ticket, &validate_list); fail_unref: if (vgdev->has_virgl_3d) { virtio_gpu_unref_list(&validate_list); @@ -374,10 +441,16 @@ static int virtio_gpu_transfer_from_host_ioctl(struct drm_device *dev, goto out_unres; convert_to_hw_box(&box, &args->box); + + fence = virtio_gpu_fence_alloc(vgdev); + if (!fence) { + ret = -ENOMEM; + goto out_unres; + } virtio_gpu_cmd_transfer_from_host_3d (vgdev, qobj->hw_res_handle, vfpriv->ctx_id, offset, args->level, - &box, &fence); + &box, fence); reservation_object_add_excl_fence(qobj->tbo.resv, &fence->f); @@ -423,10 +496,15 @@ static int virtio_gpu_transfer_to_host_ioctl(struct drm_device *dev, void *data, (vgdev, qobj, offset, box.w, box.h, box.x, box.y, NULL); } else { + fence = virtio_gpu_fence_alloc(vgdev); + if (!fence) { + ret = -ENOMEM; + goto out_unres; + } virtio_gpu_cmd_transfer_to_host_3d (vgdev, qobj, vfpriv ? vfpriv->ctx_id : 0, offset, - args->level, &box, &fence); + args->level, &box, fence); reservation_object_add_excl_fence(qobj->tbo.resv, &fence->f); dma_fence_put(&fence->f); diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index bf609dcae224..1072064a0db2 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -28,11 +28,6 @@ #include <drm/drmP.h> #include "virtgpu_drv.h" -static int virtio_gpu_fbdev = 1; - -MODULE_PARM_DESC(fbdev, "Disable/Enable framebuffer device & console"); -module_param_named(fbdev, virtio_gpu_fbdev, int, 0400); - static void virtio_gpu_config_changed_work_func(struct work_struct *work) { struct virtio_gpu_device *vgdev = @@ -44,6 +39,8 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work) virtio_cread(vgdev->vdev, struct virtio_gpu_config, events_read, &events_read); if (events_read & VIRTIO_GPU_EVENT_DISPLAY) { + if (vgdev->has_edid) + virtio_gpu_cmd_get_edids(vgdev); virtio_gpu_cmd_get_display_info(vgdev); drm_helper_hpd_irq_event(vgdev->ddev); events_clear |= VIRTIO_GPU_EVENT_DISPLAY; @@ -55,10 +52,11 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work) static int virtio_gpu_context_create(struct virtio_gpu_device *vgdev, uint32_t nlen, const char *name) { - int handle = ida_alloc_min(&vgdev->ctx_id_ida, 1, GFP_KERNEL); + int handle = ida_alloc(&vgdev->ctx_id_ida, GFP_KERNEL); if (handle < 0) return handle; + handle += 1; virtio_gpu_cmd_context_create(vgdev, handle, nlen, name); return handle; } @@ -67,7 +65,7 @@ static void virtio_gpu_context_destroy(struct virtio_gpu_device *vgdev, uint32_t ctx_id) { virtio_gpu_cmd_context_destroy(vgdev, ctx_id); - ida_free(&vgdev->ctx_id_ida, ctx_id); + ida_free(&vgdev->ctx_id_ida, ctx_id - 1); } static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq, @@ -155,6 +153,10 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags) #else DRM_INFO("virgl 3d acceleration not supported by guest\n"); #endif + if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_EDID)) { + vgdev->has_edid = true; + DRM_INFO("EDID support available.\n"); + } ret = virtio_find_vqs(vgdev->vdev, 2, vqs, callbacks, names, NULL); if (ret) { @@ -200,12 +202,11 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags) if (num_capsets) virtio_gpu_get_capsets(vgdev, num_capsets); + if (vgdev->has_edid) + virtio_gpu_cmd_get_edids(vgdev); virtio_gpu_cmd_get_display_info(vgdev); wait_event_timeout(vgdev->resp_wq, !vgdev->display_info_pending, 5 * HZ); - if (virtio_gpu_fbdev) - virtio_gpu_fbdev_init(vgdev); - return 0; err_modeset: @@ -266,8 +267,10 @@ int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file) get_task_comm(dbgname, current); id = virtio_gpu_context_create(vgdev, strlen(dbgname), dbgname); - if (id < 0) + if (id < 0) { + kfree(vfpriv); return id; + } vfpriv->ctx_id = id; file->driver_priv = vfpriv; diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index 77eac4eb06b1..f39a183d59c2 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -25,16 +25,21 @@ #include "virtgpu_drv.h" -static void virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, +static int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, uint32_t *resid) { - int handle = ida_alloc_min(&vgdev->resource_ida, 1, GFP_KERNEL); - *resid = handle; + int handle = ida_alloc(&vgdev->resource_ida, GFP_KERNEL); + + if (handle < 0) + return handle; + + *resid = handle + 1; + return 0; } static void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t id) { - ida_free(&vgdev->resource_ida, id); + ida_free(&vgdev->resource_ida, id - 1); } static void virtio_gpu_ttm_bo_destroy(struct ttm_buffer_object *tbo) @@ -94,7 +99,11 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, bo = kzalloc(sizeof(struct virtio_gpu_object), GFP_KERNEL); if (bo == NULL) return -ENOMEM; - virtio_gpu_resource_id_get(vgdev, &bo->hw_res_handle); + ret = virtio_gpu_resource_id_get(vgdev, &bo->hw_res_handle); + if (ret < 0) { + kfree(bo); + return ret; + } size = roundup(size, PAGE_SIZE); ret = drm_gem_object_init(vgdev->ddev, &bo->gem_base, size); if (ret != 0) { diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c index a9f4ae7d4483..ead5c53d4e21 100644 --- a/drivers/gpu/drm/virtio/virtgpu_plane.c +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c @@ -137,6 +137,41 @@ static void virtio_gpu_primary_plane_update(struct drm_plane *plane, plane->state->src_h >> 16); } +static int virtio_gpu_cursor_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_device *dev = plane->dev; + struct virtio_gpu_device *vgdev = dev->dev_private; + struct virtio_gpu_framebuffer *vgfb; + struct virtio_gpu_object *bo; + + if (!new_state->fb) + return 0; + + vgfb = to_virtio_gpu_framebuffer(new_state->fb); + bo = gem_to_virtio_gpu_obj(vgfb->base.obj[0]); + if (bo && bo->dumb && (plane->state->fb != new_state->fb)) { + vgfb->fence = virtio_gpu_fence_alloc(vgdev); + if (!vgfb->fence) + return -ENOMEM; + } + + return 0; +} + +static void virtio_gpu_cursor_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct virtio_gpu_framebuffer *vgfb; + + if (!plane->state->fb) + return; + + vgfb = to_virtio_gpu_framebuffer(plane->state->fb); + if (vgfb->fence) + virtio_gpu_fence_cleanup(vgfb->fence); +} + static void virtio_gpu_cursor_plane_update(struct drm_plane *plane, struct drm_plane_state *old_state) { @@ -144,7 +179,6 @@ static void virtio_gpu_cursor_plane_update(struct drm_plane *plane, struct virtio_gpu_device *vgdev = dev->dev_private; struct virtio_gpu_output *output = NULL; struct virtio_gpu_framebuffer *vgfb; - struct virtio_gpu_fence *fence = NULL; struct virtio_gpu_object *bo = NULL; uint32_t handle; int ret = 0; @@ -170,13 +204,13 @@ static void virtio_gpu_cursor_plane_update(struct drm_plane *plane, (vgdev, bo, 0, cpu_to_le32(plane->state->crtc_w), cpu_to_le32(plane->state->crtc_h), - 0, 0, &fence); + 0, 0, vgfb->fence); ret = virtio_gpu_object_reserve(bo, false); if (!ret) { reservation_object_add_excl_fence(bo->tbo.resv, - &fence->f); - dma_fence_put(&fence->f); - fence = NULL; + &vgfb->fence->f); + dma_fence_put(&vgfb->fence->f); + vgfb->fence = NULL; virtio_gpu_object_unreserve(bo); virtio_gpu_object_wait(bo, false); } @@ -218,6 +252,8 @@ static const struct drm_plane_helper_funcs virtio_gpu_primary_helper_funcs = { }; static const struct drm_plane_helper_funcs virtio_gpu_cursor_helper_funcs = { + .prepare_fb = virtio_gpu_cursor_prepare_fb, + .cleanup_fb = virtio_gpu_cursor_cleanup_fb, .atomic_check = virtio_gpu_plane_atomic_check, .atomic_update = virtio_gpu_cursor_plane_update, }; diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 51bef1775e47..e27c4aedb809 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -298,7 +298,7 @@ static int virtio_gpu_queue_ctrl_buffer(struct virtio_gpu_device *vgdev, static int virtio_gpu_queue_fenced_ctrl_buffer(struct virtio_gpu_device *vgdev, struct virtio_gpu_vbuffer *vbuf, struct virtio_gpu_ctrl_hdr *hdr, - struct virtio_gpu_fence **fence) + struct virtio_gpu_fence *fence) { struct virtqueue *vq = vgdev->ctrlq.vq; int rc; @@ -405,7 +405,7 @@ void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev, static void virtio_gpu_cmd_resource_inval_backing(struct virtio_gpu_device *vgdev, uint32_t resource_id, - struct virtio_gpu_fence **fence) + struct virtio_gpu_fence *fence) { struct virtio_gpu_resource_detach_backing *cmd_p; struct virtio_gpu_vbuffer *vbuf; @@ -467,7 +467,7 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev, uint64_t offset, __le32 width, __le32 height, __le32 x, __le32 y, - struct virtio_gpu_fence **fence) + struct virtio_gpu_fence *fence) { struct virtio_gpu_transfer_to_host_2d *cmd_p; struct virtio_gpu_vbuffer *vbuf; @@ -497,7 +497,7 @@ virtio_gpu_cmd_resource_attach_backing(struct virtio_gpu_device *vgdev, uint32_t resource_id, struct virtio_gpu_mem_entry *ents, uint32_t nents, - struct virtio_gpu_fence **fence) + struct virtio_gpu_fence *fence) { struct virtio_gpu_resource_attach_backing *cmd_p; struct virtio_gpu_vbuffer *vbuf; @@ -584,6 +584,45 @@ static void virtio_gpu_cmd_capset_cb(struct virtio_gpu_device *vgdev, wake_up(&vgdev->resp_wq); } +static int virtio_get_edid_block(void *data, u8 *buf, + unsigned int block, size_t len) +{ + struct virtio_gpu_resp_edid *resp = data; + size_t start = block * EDID_LENGTH; + + if (start + len > le32_to_cpu(resp->size)) + return -1; + memcpy(buf, resp->edid + start, len); + return 0; +} + +static void virtio_gpu_cmd_get_edid_cb(struct virtio_gpu_device *vgdev, + struct virtio_gpu_vbuffer *vbuf) +{ + struct virtio_gpu_cmd_get_edid *cmd = + (struct virtio_gpu_cmd_get_edid *)vbuf->buf; + struct virtio_gpu_resp_edid *resp = + (struct virtio_gpu_resp_edid *)vbuf->resp_buf; + uint32_t scanout = le32_to_cpu(cmd->scanout); + struct virtio_gpu_output *output; + struct edid *new_edid, *old_edid; + + if (scanout >= vgdev->num_scanouts) + return; + output = vgdev->outputs + scanout; + + new_edid = drm_do_get_edid(&output->conn, virtio_get_edid_block, resp); + + spin_lock(&vgdev->display_info_lock); + old_edid = output->edid; + output->edid = new_edid; + drm_connector_update_edid_property(&output->conn, output->edid); + spin_unlock(&vgdev->display_info_lock); + + kfree(old_edid); + wake_up(&vgdev->resp_wq); +} + int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev) { struct virtio_gpu_ctrl_hdr *cmd_p; @@ -686,6 +725,34 @@ int virtio_gpu_cmd_get_capset(struct virtio_gpu_device *vgdev, return 0; } +int virtio_gpu_cmd_get_edids(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_cmd_get_edid *cmd_p; + struct virtio_gpu_vbuffer *vbuf; + void *resp_buf; + int scanout; + + if (WARN_ON(!vgdev->has_edid)) + return -EINVAL; + + for (scanout = 0; scanout < vgdev->num_scanouts; scanout++) { + resp_buf = kzalloc(sizeof(struct virtio_gpu_resp_edid), + GFP_KERNEL); + if (!resp_buf) + return -ENOMEM; + + cmd_p = virtio_gpu_alloc_cmd_resp + (vgdev, &virtio_gpu_cmd_get_edid_cb, &vbuf, + sizeof(*cmd_p), sizeof(struct virtio_gpu_resp_edid), + resp_buf); + cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_GET_EDID); + cmd_p->scanout = cpu_to_le32(scanout); + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); + } + + return 0; +} + void virtio_gpu_cmd_context_create(struct virtio_gpu_device *vgdev, uint32_t id, uint32_t nlen, const char *name) { @@ -753,8 +820,7 @@ void virtio_gpu_cmd_context_detach_resource(struct virtio_gpu_device *vgdev, void virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *bo, - struct virtio_gpu_resource_create_3d *rc_3d, - struct virtio_gpu_fence **fence) + struct virtio_gpu_resource_create_3d *rc_3d) { struct virtio_gpu_resource_create_3d *cmd_p; struct virtio_gpu_vbuffer *vbuf; @@ -766,7 +832,7 @@ virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev, cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_CREATE_3D); cmd_p->hdr.flags = 0; - virtio_gpu_queue_fenced_ctrl_buffer(vgdev, vbuf, &cmd_p->hdr, fence); + virtio_gpu_queue_ctrl_buffer(vgdev, vbuf); bo->created = true; } @@ -775,7 +841,7 @@ void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev, uint32_t ctx_id, uint64_t offset, uint32_t level, struct virtio_gpu_box *box, - struct virtio_gpu_fence **fence) + struct virtio_gpu_fence *fence) { struct virtio_gpu_transfer_host_3d *cmd_p; struct virtio_gpu_vbuffer *vbuf; @@ -803,7 +869,7 @@ void virtio_gpu_cmd_transfer_from_host_3d(struct virtio_gpu_device *vgdev, uint32_t resource_id, uint32_t ctx_id, uint64_t offset, uint32_t level, struct virtio_gpu_box *box, - struct virtio_gpu_fence **fence) + struct virtio_gpu_fence *fence) { struct virtio_gpu_transfer_host_3d *cmd_p; struct virtio_gpu_vbuffer *vbuf; @@ -823,7 +889,7 @@ void virtio_gpu_cmd_transfer_from_host_3d(struct virtio_gpu_device *vgdev, void virtio_gpu_cmd_submit(struct virtio_gpu_device *vgdev, void *data, uint32_t data_size, - uint32_t ctx_id, struct virtio_gpu_fence **fence) + uint32_t ctx_id, struct virtio_gpu_fence *fence) { struct virtio_gpu_cmd_submit *cmd_p; struct virtio_gpu_vbuffer *vbuf; @@ -843,7 +909,7 @@ void virtio_gpu_cmd_submit(struct virtio_gpu_device *vgdev, int virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *obj, - struct virtio_gpu_fence **fence) + struct virtio_gpu_fence *fence) { bool use_dma_api = !virtio_has_iommu_quirk(vgdev->vdev); struct virtio_gpu_mem_entry *ents; @@ -896,11 +962,11 @@ void virtio_gpu_object_detach(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *obj) { bool use_dma_api = !virtio_has_iommu_quirk(vgdev->vdev); - struct virtio_gpu_fence *fence; if (use_dma_api && obj->mapped) { + struct virtio_gpu_fence *fence = virtio_gpu_fence_alloc(vgdev); /* detach backing and wait for the host process it ... */ - virtio_gpu_cmd_resource_inval_backing(vgdev, obj->hw_res_handle, &fence); + virtio_gpu_cmd_resource_inval_backing(vgdev, obj->hw_res_handle, fence); dma_fence_wait(&fence->f, true); dma_fence_put(&fence->f); diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index a3d57e0f5ee5..2a16b86196dc 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -68,7 +68,6 @@ static struct drm_driver vkms_driver = { .release = vkms_release, .fops = &vkms_driver_fops, .dumb_create = vkms_dumb_create, - .dumb_map_offset = vkms_dumb_map, .gem_vm_ops = &vkms_gem_vm_ops, .gem_free_object_unlocked = vkms_gem_free_object, .get_vblank_timestamp = vkms_get_vblank_timestamp, @@ -96,6 +95,7 @@ static int vkms_modeset_init(struct vkms_device *vkmsdev) dev->mode_config.min_height = YRES_MIN; dev->mode_config.max_width = XRES_MAX; dev->mode_config.max_height = YRES_MAX; + dev->mode_config.preferred_depth = 24; return vkms_output_init(vkmsdev); } diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index 1c93990693e3..e4469cd3d254 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -127,9 +127,6 @@ vm_fault_t vkms_gem_fault(struct vm_fault *vmf); int vkms_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args); -int vkms_dumb_map(struct drm_file *file, struct drm_device *dev, - u32 handle, u64 *offset); - void vkms_gem_free_object(struct drm_gem_object *obj); int vkms_gem_vmap(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/vkms/vkms_gem.c b/drivers/gpu/drm/vkms/vkms_gem.c index d04e988b4cbe..80311daed47a 100644 --- a/drivers/gpu/drm/vkms/vkms_gem.c +++ b/drivers/gpu/drm/vkms/vkms_gem.c @@ -153,32 +153,6 @@ int vkms_dumb_create(struct drm_file *file, struct drm_device *dev, return 0; } -int vkms_dumb_map(struct drm_file *file, struct drm_device *dev, - u32 handle, u64 *offset) -{ - struct drm_gem_object *obj; - int ret; - - obj = drm_gem_object_lookup(file, handle); - if (!obj) - return -ENOENT; - - if (!obj->filp) { - ret = -EINVAL; - goto unref; - } - - ret = drm_gem_create_mmap_offset(obj); - if (ret) - goto unref; - - *offset = drm_vma_node_offset_addr(&obj->vma_node); -unref: - drm_gem_object_put_unlocked(obj); - - return ret; -} - static struct page **_get_pages(struct vkms_gem_object *vkms_obj) { struct drm_gem_object *gem_obj = &vkms_obj->gem; diff --git a/drivers/gpu/drm/vkms/vkms_plane.c b/drivers/gpu/drm/vkms/vkms_plane.c index 7041007396ae..418817600ad1 100644 --- a/drivers/gpu/drm/vkms/vkms_plane.c +++ b/drivers/gpu/drm/vkms/vkms_plane.c @@ -23,8 +23,11 @@ vkms_plane_duplicate_state(struct drm_plane *plane) return NULL; crc_data = kzalloc(sizeof(*crc_data), GFP_KERNEL); - if (WARN_ON(!crc_data)) - DRM_INFO("Couldn't allocate crc_data"); + if (!crc_data) { + DRM_DEBUG_KMS("Couldn't allocate crc_data\n"); + kfree(vkms_state); + return NULL; + } vkms_state->crc_data = crc_data; @@ -138,14 +141,12 @@ static int vkms_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state) { struct drm_gem_object *gem_obj; - struct vkms_gem_object *vkms_obj; int ret; if (!state->fb) return 0; gem_obj = drm_gem_fb_get_obj(state->fb, 0); - vkms_obj = drm_gem_to_vkms_gem(gem_obj); ret = vkms_gem_vmap(gem_obj); if (ret) DRM_ERROR("vmap failed: %d\n", ret); diff --git a/drivers/gpu/drm/xen/xen_drm_front_conn.c b/drivers/gpu/drm/xen/xen_drm_front_conn.c index c91ae532fa55..54af2669b1b3 100644 --- a/drivers/gpu/drm/xen/xen_drm_front_conn.c +++ b/drivers/gpu/drm/xen/xen_drm_front_conn.c @@ -89,7 +89,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { }; static const struct drm_connector_funcs connector_funcs = { - .dpms = drm_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index c0d668944dbe..ed35c9a9a110 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -275,6 +275,9 @@ #define USB_VENDOR_ID_CIDC 0x1677 +#define I2C_VENDOR_ID_CIRQUE 0x0488 +#define I2C_PRODUCT_ID_CIRQUE_121F 0x121F + #define USB_VENDOR_ID_CJTOUCH 0x24b8 #define USB_DEVICE_ID_CJTOUCH_MULTI_TOUCH_0020 0x0020 #define USB_DEVICE_ID_CJTOUCH_MULTI_TOUCH_0040 0x0040 @@ -707,6 +710,7 @@ #define USB_VENDOR_ID_LG 0x1fd2 #define USB_DEVICE_ID_LG_MULTITOUCH 0x0064 #define USB_DEVICE_ID_LG_MELFAS_MT 0x6007 +#define I2C_DEVICE_ID_LG_8001 0x8001 #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e @@ -805,6 +809,7 @@ #define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 #define USB_DEVICE_ID_MS_POWER_COVER 0x07da #define USB_DEVICE_ID_MS_XBOX_ONE_S_CONTROLLER 0x02fd +#define USB_DEVICE_ID_MS_PIXART_MOUSE 0x00cb #define USB_VENDOR_ID_MOJO 0x8282 #define USB_DEVICE_ID_RETRO_ADAPTER 0x3201 @@ -1043,6 +1048,7 @@ #define USB_VENDOR_ID_SYMBOL 0x05e0 #define USB_DEVICE_ID_SYMBOL_SCANNER_1 0x0800 #define USB_DEVICE_ID_SYMBOL_SCANNER_2 0x1300 +#define USB_DEVICE_ID_SYMBOL_SCANNER_3 0x1200 #define USB_VENDOR_ID_SYNAPTICS 0x06cb #define USB_DEVICE_ID_SYNAPTICS_TP 0x0001 @@ -1204,6 +1210,8 @@ #define USB_DEVICE_ID_PRIMAX_MOUSE_4D22 0x4d22 #define USB_DEVICE_ID_PRIMAX_KEYBOARD 0x4e05 #define USB_DEVICE_ID_PRIMAX_REZEL 0x4e72 +#define USB_DEVICE_ID_PRIMAX_PIXART_MOUSE_4D0F 0x4d0f +#define USB_DEVICE_ID_PRIMAX_PIXART_MOUSE_4E22 0x4e22 #define USB_VENDOR_ID_RISO_KAGAKU 0x1294 /* Riso Kagaku Corp. */ diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index a2f74e6adc70..d6fab5798487 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -325,6 +325,9 @@ static const struct hid_device_id hid_battery_quirks[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084), HID_BATTERY_QUIRK_IGNORE }, + { HID_USB_DEVICE(USB_VENDOR_ID_SYMBOL, + USB_DEVICE_ID_SYMBOL_SCANNER_3), + HID_BATTERY_QUIRK_IGNORE }, {} }; @@ -1838,47 +1841,3 @@ void hidinput_disconnect(struct hid_device *hid) } EXPORT_SYMBOL_GPL(hidinput_disconnect); -/** - * hid_scroll_counter_handle_scroll() - Send high- and low-resolution scroll - * events given a high-resolution wheel - * movement. - * @counter: a hid_scroll_counter struct describing the wheel. - * @hi_res_value: the movement of the wheel, in the mouse's high-resolution - * units. - * - * Given a high-resolution movement, this function converts the movement into - * microns and emits high-resolution scroll events for the input device. It also - * uses the multiplier from &struct hid_scroll_counter to emit low-resolution - * scroll events when appropriate for backwards-compatibility with userspace - * input libraries. - */ -void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter, - int hi_res_value) -{ - int low_res_value, remainder, multiplier; - - input_report_rel(counter->dev, REL_WHEEL_HI_RES, - hi_res_value * counter->microns_per_hi_res_unit); - - /* - * Update the low-res remainder with the high-res value, - * but reset if the direction has changed. - */ - remainder = counter->remainder; - if ((remainder ^ hi_res_value) < 0) - remainder = 0; - remainder += hi_res_value; - - /* - * Then just use the resolution multiplier to see if - * we should send a low-res (aka regular wheel) event. - */ - multiplier = counter->resolution_multiplier; - low_res_value = remainder / multiplier; - remainder -= low_res_value * multiplier; - counter->remainder = remainder; - - if (low_res_value) - input_report_rel(counter->dev, REL_WHEEL, low_res_value); -} -EXPORT_SYMBOL_GPL(hid_scroll_counter_handle_scroll); diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index f01280898b24..19cc980eebce 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -64,14 +64,6 @@ MODULE_PARM_DESC(disable_tap_to_click, #define HIDPP_QUIRK_NO_HIDINPUT BIT(23) #define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24) #define HIDPP_QUIRK_UNIFYING BIT(25) -#define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(26) -#define HIDPP_QUIRK_HI_RES_SCROLL_X2120 BIT(27) -#define HIDPP_QUIRK_HI_RES_SCROLL_X2121 BIT(28) - -/* Convenience constant to check for any high-res support. */ -#define HIDPP_QUIRK_HI_RES_SCROLL (HIDPP_QUIRK_HI_RES_SCROLL_1P0 | \ - HIDPP_QUIRK_HI_RES_SCROLL_X2120 | \ - HIDPP_QUIRK_HI_RES_SCROLL_X2121) #define HIDPP_QUIRK_DELAYED_INIT HIDPP_QUIRK_NO_HIDINPUT @@ -157,7 +149,6 @@ struct hidpp_device { unsigned long capabilities; struct hidpp_battery battery; - struct hid_scroll_counter vertical_wheel_counter; }; /* HID++ 1.0 error codes */ @@ -409,53 +400,32 @@ static void hidpp_prefix_name(char **name, int name_length) #define HIDPP_SET_LONG_REGISTER 0x82 #define HIDPP_GET_LONG_REGISTER 0x83 -/** - * hidpp10_set_register_bit() - Sets a single bit in a HID++ 1.0 register. - * @hidpp_dev: the device to set the register on. - * @register_address: the address of the register to modify. - * @byte: the byte of the register to modify. Should be less than 3. - * Return: 0 if successful, otherwise a negative error code. - */ -static int hidpp10_set_register_bit(struct hidpp_device *hidpp_dev, - u8 register_address, u8 byte, u8 bit) +#define HIDPP_REG_GENERAL 0x00 + +static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev) { struct hidpp_report response; int ret; u8 params[3] = { 0 }; ret = hidpp_send_rap_command_sync(hidpp_dev, - REPORT_ID_HIDPP_SHORT, - HIDPP_GET_REGISTER, - register_address, - NULL, 0, &response); + REPORT_ID_HIDPP_SHORT, + HIDPP_GET_REGISTER, + HIDPP_REG_GENERAL, + NULL, 0, &response); if (ret) return ret; memcpy(params, response.rap.params, 3); - params[byte] |= BIT(bit); + /* Set the battery bit */ + params[0] |= BIT(4); return hidpp_send_rap_command_sync(hidpp_dev, - REPORT_ID_HIDPP_SHORT, - HIDPP_SET_REGISTER, - register_address, - params, 3, &response); -} - - -#define HIDPP_REG_GENERAL 0x00 - -static int hidpp10_enable_battery_reporting(struct hidpp_device *hidpp_dev) -{ - return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_GENERAL, 0, 4); -} - -#define HIDPP_REG_FEATURES 0x01 - -/* On HID++ 1.0 devices, high-res scroll was called "scrolling acceleration". */ -static int hidpp10_enable_scrolling_acceleration(struct hidpp_device *hidpp_dev) -{ - return hidpp10_set_register_bit(hidpp_dev, HIDPP_REG_FEATURES, 0, 6); + REPORT_ID_HIDPP_SHORT, + HIDPP_SET_REGISTER, + HIDPP_REG_GENERAL, + params, 3, &response); } #define HIDPP_REG_BATTERY_STATUS 0x07 @@ -1167,100 +1137,6 @@ static int hidpp_battery_get_property(struct power_supply *psy, } /* -------------------------------------------------------------------------- */ -/* 0x2120: Hi-resolution scrolling */ -/* -------------------------------------------------------------------------- */ - -#define HIDPP_PAGE_HI_RESOLUTION_SCROLLING 0x2120 - -#define CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE 0x10 - -static int hidpp_hrs_set_highres_scrolling_mode(struct hidpp_device *hidpp, - bool enabled, u8 *multiplier) -{ - u8 feature_index; - u8 feature_type; - int ret; - u8 params[1]; - struct hidpp_report response; - - ret = hidpp_root_get_feature(hidpp, - HIDPP_PAGE_HI_RESOLUTION_SCROLLING, - &feature_index, - &feature_type); - if (ret) - return ret; - - params[0] = enabled ? BIT(0) : 0; - ret = hidpp_send_fap_command_sync(hidpp, feature_index, - CMD_HI_RESOLUTION_SCROLLING_SET_HIGHRES_SCROLLING_MODE, - params, sizeof(params), &response); - if (ret) - return ret; - *multiplier = response.fap.params[1]; - return 0; -} - -/* -------------------------------------------------------------------------- */ -/* 0x2121: HiRes Wheel */ -/* -------------------------------------------------------------------------- */ - -#define HIDPP_PAGE_HIRES_WHEEL 0x2121 - -#define CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY 0x00 -#define CMD_HIRES_WHEEL_SET_WHEEL_MODE 0x20 - -static int hidpp_hrw_get_wheel_capability(struct hidpp_device *hidpp, - u8 *multiplier) -{ - u8 feature_index; - u8 feature_type; - int ret; - struct hidpp_report response; - - ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL, - &feature_index, &feature_type); - if (ret) - goto return_default; - - ret = hidpp_send_fap_command_sync(hidpp, feature_index, - CMD_HIRES_WHEEL_GET_WHEEL_CAPABILITY, - NULL, 0, &response); - if (ret) - goto return_default; - - *multiplier = response.fap.params[0]; - return 0; -return_default: - hid_warn(hidpp->hid_dev, - "Couldn't get wheel multiplier (error %d), assuming %d.\n", - ret, *multiplier); - return ret; -} - -static int hidpp_hrw_set_wheel_mode(struct hidpp_device *hidpp, bool invert, - bool high_resolution, bool use_hidpp) -{ - u8 feature_index; - u8 feature_type; - int ret; - u8 params[1]; - struct hidpp_report response; - - ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_HIRES_WHEEL, - &feature_index, &feature_type); - if (ret) - return ret; - - params[0] = (invert ? BIT(2) : 0) | - (high_resolution ? BIT(1) : 0) | - (use_hidpp ? BIT(0) : 0); - - return hidpp_send_fap_command_sync(hidpp, feature_index, - CMD_HIRES_WHEEL_SET_WHEEL_MODE, - params, sizeof(params), &response); -} - -/* -------------------------------------------------------------------------- */ /* 0x4301: Solar Keyboard */ /* -------------------------------------------------------------------------- */ @@ -2523,8 +2399,7 @@ static int m560_raw_event(struct hid_device *hdev, u8 *data, int size) input_report_rel(mydata->input, REL_Y, v); v = hid_snto32(data[6], 8); - hid_scroll_counter_handle_scroll( - &hidpp->vertical_wheel_counter, v); + input_report_rel(mydata->input, REL_WHEEL, v); input_sync(mydata->input); } @@ -2653,72 +2528,6 @@ static int g920_get_config(struct hidpp_device *hidpp) } /* -------------------------------------------------------------------------- */ -/* High-resolution scroll wheels */ -/* -------------------------------------------------------------------------- */ - -/** - * struct hi_res_scroll_info - Stores info on a device's high-res scroll wheel. - * @product_id: the HID product ID of the device being described. - * @microns_per_hi_res_unit: the distance moved by the user's finger for each - * high-resolution unit reported by the device, in - * 256ths of a millimetre. - */ -struct hi_res_scroll_info { - __u32 product_id; - int microns_per_hi_res_unit; -}; - -static struct hi_res_scroll_info hi_res_scroll_devices[] = { - { /* Anywhere MX */ - .product_id = 0x1017, .microns_per_hi_res_unit = 445 }, - { /* Performance MX */ - .product_id = 0x101a, .microns_per_hi_res_unit = 406 }, - { /* M560 */ - .product_id = 0x402d, .microns_per_hi_res_unit = 435 }, - { /* MX Master 2S */ - .product_id = 0x4069, .microns_per_hi_res_unit = 406 }, -}; - -static int hi_res_scroll_look_up_microns(__u32 product_id) -{ - int i; - int num_devices = sizeof(hi_res_scroll_devices) - / sizeof(hi_res_scroll_devices[0]); - for (i = 0; i < num_devices; i++) { - if (hi_res_scroll_devices[i].product_id == product_id) - return hi_res_scroll_devices[i].microns_per_hi_res_unit; - } - /* We don't have a value for this device, so use a sensible default. */ - return 406; -} - -static int hi_res_scroll_enable(struct hidpp_device *hidpp) -{ - int ret; - u8 multiplier = 8; - - if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2121) { - ret = hidpp_hrw_set_wheel_mode(hidpp, false, true, false); - hidpp_hrw_get_wheel_capability(hidpp, &multiplier); - } else if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_X2120) { - ret = hidpp_hrs_set_highres_scrolling_mode(hidpp, true, - &multiplier); - } else /* if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL_1P0) */ - ret = hidpp10_enable_scrolling_acceleration(hidpp); - - if (ret) - return ret; - - hidpp->vertical_wheel_counter.resolution_multiplier = multiplier; - hidpp->vertical_wheel_counter.microns_per_hi_res_unit = - hi_res_scroll_look_up_microns(hidpp->hid_dev->product); - hid_info(hidpp->hid_dev, "multiplier = %d, microns = %d\n", - multiplier, - hidpp->vertical_wheel_counter.microns_per_hi_res_unit); - return 0; -} - -/* -------------------------------------------------------------------------- */ /* Generic HID++ devices */ /* -------------------------------------------------------------------------- */ @@ -2763,11 +2572,6 @@ static void hidpp_populate_input(struct hidpp_device *hidpp, wtp_populate_input(hidpp, input, origin_is_hid_core); else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) m560_populate_input(hidpp, input, origin_is_hid_core); - - if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) { - input_set_capability(input, EV_REL, REL_WHEEL_HI_RES); - hidpp->vertical_wheel_counter.dev = input; - } } static int hidpp_input_configured(struct hid_device *hdev, @@ -2886,27 +2690,6 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report, return 0; } -static int hidpp_event(struct hid_device *hdev, struct hid_field *field, - struct hid_usage *usage, __s32 value) -{ - /* This function will only be called for scroll events, due to the - * restriction imposed in hidpp_usages. - */ - struct hidpp_device *hidpp = hid_get_drvdata(hdev); - struct hid_scroll_counter *counter = &hidpp->vertical_wheel_counter; - /* A scroll event may occur before the multiplier has been retrieved or - * the input device set, or high-res scroll enabling may fail. In such - * cases we must return early (falling back to default behaviour) to - * avoid a crash in hid_scroll_counter_handle_scroll. - */ - if (!(hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) || value == 0 - || counter->dev == NULL || counter->resolution_multiplier == 0) - return 0; - - hid_scroll_counter_handle_scroll(counter, value); - return 1; -} - static int hidpp_initialize_battery(struct hidpp_device *hidpp) { static atomic_t battery_no = ATOMIC_INIT(0); @@ -3118,9 +2901,6 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) if (hidpp->battery.ps) power_supply_changed(hidpp->battery.ps); - if (hidpp->quirks & HIDPP_QUIRK_HI_RES_SCROLL) - hi_res_scroll_enable(hidpp); - if (!(hidpp->quirks & HIDPP_QUIRK_NO_HIDINPUT) || hidpp->delayed_input) /* if the input nodes are already created, we can stop now */ return; @@ -3306,63 +3086,35 @@ static void hidpp_remove(struct hid_device *hdev) mutex_destroy(&hidpp->send_mutex); } -#define LDJ_DEVICE(product) \ - HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, \ - USB_VENDOR_ID_LOGITECH, (product)) - static const struct hid_device_id hidpp_devices[] = { { /* wireless touchpad */ - LDJ_DEVICE(0x4011), + HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, + USB_VENDOR_ID_LOGITECH, 0x4011), .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS }, { /* wireless touchpad T650 */ - LDJ_DEVICE(0x4101), + HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, + USB_VENDOR_ID_LOGITECH, 0x4101), .driver_data = HIDPP_QUIRK_CLASS_WTP | HIDPP_QUIRK_DELAYED_INIT }, { /* wireless touchpad T651 */ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_T651), .driver_data = HIDPP_QUIRK_CLASS_WTP }, - { /* Mouse Logitech Anywhere MX */ - LDJ_DEVICE(0x1017), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 }, - { /* Mouse Logitech Cube */ - LDJ_DEVICE(0x4010), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 }, - { /* Mouse Logitech M335 */ - LDJ_DEVICE(0x4050), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, - { /* Mouse Logitech M515 */ - LDJ_DEVICE(0x4007), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2120 }, { /* Mouse logitech M560 */ - LDJ_DEVICE(0x402d), - .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 - | HIDPP_QUIRK_HI_RES_SCROLL_X2120 }, - { /* Mouse Logitech M705 (firmware RQM17) */ - LDJ_DEVICE(0x101b), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 }, - { /* Mouse Logitech M705 (firmware RQM67) */ - LDJ_DEVICE(0x406d), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, - { /* Mouse Logitech M720 */ - LDJ_DEVICE(0x405e), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, - { /* Mouse Logitech MX Anywhere 2 */ - LDJ_DEVICE(0x404a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, - { LDJ_DEVICE(0xb013), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, - { LDJ_DEVICE(0xb018), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, - { LDJ_DEVICE(0xb01f), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, - { /* Mouse Logitech MX Anywhere 2S */ - LDJ_DEVICE(0x406a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, - { /* Mouse Logitech MX Master */ - LDJ_DEVICE(0x4041), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, - { LDJ_DEVICE(0x4060), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, - { LDJ_DEVICE(0x4071), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, - { /* Mouse Logitech MX Master 2S */ - LDJ_DEVICE(0x4069), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_X2121 }, - { /* Mouse Logitech Performance MX */ - LDJ_DEVICE(0x101a), .driver_data = HIDPP_QUIRK_HI_RES_SCROLL_1P0 }, + HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, + USB_VENDOR_ID_LOGITECH, 0x402d), + .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_CLASS_M560 }, { /* Keyboard logitech K400 */ - LDJ_DEVICE(0x4024), + HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, + USB_VENDOR_ID_LOGITECH, 0x4024), .driver_data = HIDPP_QUIRK_CLASS_K400 }, { /* Solar Keyboard Logitech K750 */ - LDJ_DEVICE(0x4002), + HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, + USB_VENDOR_ID_LOGITECH, 0x4002), .driver_data = HIDPP_QUIRK_CLASS_K750 }, - { LDJ_DEVICE(HID_ANY_ID) }, + { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, + USB_VENDOR_ID_LOGITECH, HID_ANY_ID)}, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G920_WHEEL), .driver_data = HIDPP_QUIRK_CLASS_G920 | HIDPP_QUIRK_FORCE_OUTPUT_REPORTS}, @@ -3371,19 +3123,12 @@ static const struct hid_device_id hidpp_devices[] = { MODULE_DEVICE_TABLE(hid, hidpp_devices); -static const struct hid_usage_id hidpp_usages[] = { - { HID_GD_WHEEL, EV_REL, REL_WHEEL }, - { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1} -}; - static struct hid_driver hidpp_driver = { .name = "logitech-hidpp-device", .id_table = hidpp_devices, .probe = hidpp_probe, .remove = hidpp_remove, .raw_event = hidpp_raw_event, - .usage_table = hidpp_usages, - .event = hidpp_event, .input_configured = hidpp_input_configured, .input_mapping = hidpp_input_mapping, .input_mapped = hidpp_input_mapped, diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index f7c6de2b6730..dca0a3a90fb8 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -1814,6 +1814,12 @@ static const struct hid_device_id mt_devices[] = { MT_USB_DEVICE(USB_VENDOR_ID_CHUNGHWAT, USB_DEVICE_ID_CHUNGHWAT_MULTITOUCH) }, + /* Cirque devices */ + { .driver_data = MT_CLS_WIN_8_DUAL, + HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8, + I2C_VENDOR_ID_CIRQUE, + I2C_PRODUCT_ID_CIRQUE_121F) }, + /* CJTouch panels */ { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_CJTOUCH, diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 8237dd86fb17..c85a79986b6a 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -107,6 +107,7 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C05A), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOUSE_C06A), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_MCS, USB_DEVICE_ID_MCS_GAMEPADBLOCK), HID_QUIRK_MULTI_INPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PIXART_MOUSE), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_POWER_COVER), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_SURFACE_PRO_2), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TOUCH_COVER_2), HID_QUIRK_NO_INIT_REPORTS }, @@ -129,6 +130,8 @@ static const struct hid_device_id hid_quirks[] = { { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN), HID_QUIRK_NO_INIT_REPORTS }, { HID_USB_DEVICE(USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_USB_OPTICAL_MOUSE), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_MOUSE_4D22), HID_QUIRK_ALWAYS_POLL }, + { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_PIXART_MOUSE_4D0F), HID_QUIRK_ALWAYS_POLL }, + { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_PIXART_MOUSE_4E22), HID_QUIRK_ALWAYS_POLL }, { HID_USB_DEVICE(USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS), HID_QUIRK_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001), HID_QUIRK_NOGET }, { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3003), HID_QUIRK_NOGET }, diff --git a/drivers/hid/hid-steam.c b/drivers/hid/hid-steam.c index 0422ec2b13d2..dc4128bfe2ca 100644 --- a/drivers/hid/hid-steam.c +++ b/drivers/hid/hid-steam.c @@ -23,8 +23,9 @@ * In order to avoid breaking them this driver creates a layered hidraw device, * so it can detect when the client is running and then: * - it will not send any command to the controller. - * - this input device will be disabled, to avoid double input of the same + * - this input device will be removed, to avoid double input of the same * user action. + * When the client is closed, this input device will be created again. * * For additional functions, such as changing the right-pad margin or switching * the led, you can use the user-space tool at: @@ -113,7 +114,7 @@ struct steam_device { spinlock_t lock; struct hid_device *hdev, *client_hdev; struct mutex mutex; - bool client_opened, input_opened; + bool client_opened; struct input_dev __rcu *input; unsigned long quirks; struct work_struct work_connect; @@ -279,18 +280,6 @@ static void steam_set_lizard_mode(struct steam_device *steam, bool enable) } } -static void steam_update_lizard_mode(struct steam_device *steam) -{ - mutex_lock(&steam->mutex); - if (!steam->client_opened) { - if (steam->input_opened) - steam_set_lizard_mode(steam, false); - else - steam_set_lizard_mode(steam, lizard_mode); - } - mutex_unlock(&steam->mutex); -} - static int steam_input_open(struct input_dev *dev) { struct steam_device *steam = input_get_drvdata(dev); @@ -301,7 +290,6 @@ static int steam_input_open(struct input_dev *dev) return ret; mutex_lock(&steam->mutex); - steam->input_opened = true; if (!steam->client_opened && lizard_mode) steam_set_lizard_mode(steam, false); mutex_unlock(&steam->mutex); @@ -313,7 +301,6 @@ static void steam_input_close(struct input_dev *dev) struct steam_device *steam = input_get_drvdata(dev); mutex_lock(&steam->mutex); - steam->input_opened = false; if (!steam->client_opened && lizard_mode) steam_set_lizard_mode(steam, true); mutex_unlock(&steam->mutex); @@ -400,7 +387,7 @@ static int steam_battery_register(struct steam_device *steam) return 0; } -static int steam_register(struct steam_device *steam) +static int steam_input_register(struct steam_device *steam) { struct hid_device *hdev = steam->hdev; struct input_dev *input; @@ -414,17 +401,6 @@ static int steam_register(struct steam_device *steam) return 0; } - /* - * Unlikely, but getting the serial could fail, and it is not so - * important, so make up a serial number and go on. - */ - if (steam_get_serial(steam) < 0) - strlcpy(steam->serial_no, "XXXXXXXXXX", - sizeof(steam->serial_no)); - - hid_info(hdev, "Steam Controller '%s' connected", - steam->serial_no); - input = input_allocate_device(); if (!input) return -ENOMEM; @@ -492,11 +468,6 @@ static int steam_register(struct steam_device *steam) goto input_register_fail; rcu_assign_pointer(steam->input, input); - - /* ignore battery errors, we can live without it */ - if (steam->quirks & STEAM_QUIRK_WIRELESS) - steam_battery_register(steam); - return 0; input_register_fail: @@ -504,27 +475,88 @@ input_register_fail: return ret; } -static void steam_unregister(struct steam_device *steam) +static void steam_input_unregister(struct steam_device *steam) { struct input_dev *input; + rcu_read_lock(); + input = rcu_dereference(steam->input); + rcu_read_unlock(); + if (!input) + return; + RCU_INIT_POINTER(steam->input, NULL); + synchronize_rcu(); + input_unregister_device(input); +} + +static void steam_battery_unregister(struct steam_device *steam) +{ struct power_supply *battery; rcu_read_lock(); - input = rcu_dereference(steam->input); battery = rcu_dereference(steam->battery); rcu_read_unlock(); - if (battery) { - RCU_INIT_POINTER(steam->battery, NULL); - synchronize_rcu(); - power_supply_unregister(battery); + if (!battery) + return; + RCU_INIT_POINTER(steam->battery, NULL); + synchronize_rcu(); + power_supply_unregister(battery); +} + +static int steam_register(struct steam_device *steam) +{ + int ret; + + /* + * This function can be called several times in a row with the + * wireless adaptor, without steam_unregister() between them, because + * another client send a get_connection_status command, for example. + * The battery and serial number are set just once per device. + */ + if (!steam->serial_no[0]) { + /* + * Unlikely, but getting the serial could fail, and it is not so + * important, so make up a serial number and go on. + */ + if (steam_get_serial(steam) < 0) + strlcpy(steam->serial_no, "XXXXXXXXXX", + sizeof(steam->serial_no)); + + hid_info(steam->hdev, "Steam Controller '%s' connected", + steam->serial_no); + + /* ignore battery errors, we can live without it */ + if (steam->quirks & STEAM_QUIRK_WIRELESS) + steam_battery_register(steam); + + mutex_lock(&steam_devices_lock); + list_add(&steam->list, &steam_devices); + mutex_unlock(&steam_devices_lock); } - if (input) { - RCU_INIT_POINTER(steam->input, NULL); - synchronize_rcu(); + + mutex_lock(&steam->mutex); + if (!steam->client_opened) { + steam_set_lizard_mode(steam, lizard_mode); + ret = steam_input_register(steam); + } else { + ret = 0; + } + mutex_unlock(&steam->mutex); + + return ret; +} + +static void steam_unregister(struct steam_device *steam) +{ + steam_battery_unregister(steam); + steam_input_unregister(steam); + if (steam->serial_no[0]) { hid_info(steam->hdev, "Steam Controller '%s' disconnected", steam->serial_no); - input_unregister_device(input); + mutex_lock(&steam_devices_lock); + list_del(&steam->list); + mutex_unlock(&steam_devices_lock); + steam->serial_no[0] = 0; } } @@ -600,6 +632,9 @@ static int steam_client_ll_open(struct hid_device *hdev) mutex_lock(&steam->mutex); steam->client_opened = true; mutex_unlock(&steam->mutex); + + steam_input_unregister(steam); + return ret; } @@ -609,13 +644,13 @@ static void steam_client_ll_close(struct hid_device *hdev) mutex_lock(&steam->mutex); steam->client_opened = false; - if (steam->input_opened) - steam_set_lizard_mode(steam, false); - else - steam_set_lizard_mode(steam, lizard_mode); mutex_unlock(&steam->mutex); hid_hw_close(steam->hdev); + if (steam->connected) { + steam_set_lizard_mode(steam, lizard_mode); + steam_input_register(steam); + } } static int steam_client_ll_raw_request(struct hid_device *hdev, @@ -744,11 +779,6 @@ static int steam_probe(struct hid_device *hdev, } } - mutex_lock(&steam_devices_lock); - steam_update_lizard_mode(steam); - list_add(&steam->list, &steam_devices); - mutex_unlock(&steam_devices_lock); - return 0; hid_hw_open_fail: @@ -774,10 +804,6 @@ static void steam_remove(struct hid_device *hdev) return; } - mutex_lock(&steam_devices_lock); - list_del(&steam->list); - mutex_unlock(&steam_devices_lock); - hid_destroy_device(steam->client_hdev); steam->client_opened = false; cancel_work_sync(&steam->work_connect); @@ -792,12 +818,14 @@ static void steam_remove(struct hid_device *hdev) static void steam_do_connect_event(struct steam_device *steam, bool connected) { unsigned long flags; + bool changed; spin_lock_irqsave(&steam->lock, flags); + changed = steam->connected != connected; steam->connected = connected; spin_unlock_irqrestore(&steam->lock, flags); - if (schedule_work(&steam->work_connect) == 0) + if (changed && schedule_work(&steam->work_connect) == 0) dbg_hid("%s: connected=%d event already queued\n", __func__, connected); } @@ -1019,13 +1047,8 @@ static int steam_raw_event(struct hid_device *hdev, return 0; rcu_read_lock(); input = rcu_dereference(steam->input); - if (likely(input)) { + if (likely(input)) steam_do_input_event(steam, input, data); - } else { - dbg_hid("%s: input data without connect event\n", - __func__); - steam_do_connect_event(steam, true); - } rcu_read_unlock(); break; case STEAM_EV_CONNECT: @@ -1074,7 +1097,10 @@ static int steam_param_set_lizard_mode(const char *val, mutex_lock(&steam_devices_lock); list_for_each_entry(steam, &steam_devices, list) { - steam_update_lizard_mode(steam); + mutex_lock(&steam->mutex); + if (!steam->client_opened) + steam_set_lizard_mode(steam, lizard_mode); + mutex_unlock(&steam->mutex); } mutex_unlock(&steam_devices_lock); return 0; diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index 3cde7c1b9c33..8555ce7e737b 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -177,6 +177,8 @@ static const struct i2c_hid_quirks { I2C_HID_QUIRK_NO_RUNTIME_PM }, { I2C_VENDOR_ID_RAYDIUM, I2C_PRODUCT_ID_RAYDIUM_4B33, I2C_HID_QUIRK_DELAY_AFTER_SLEEP }, + { USB_VENDOR_ID_LG, I2C_DEVICE_ID_LG_8001, + I2C_HID_QUIRK_NO_RUNTIME_PM }, { 0, 0 } }; diff --git a/drivers/hid/uhid.c b/drivers/hid/uhid.c index 3c5507313606..840634e0f1e3 100644 --- a/drivers/hid/uhid.c +++ b/drivers/hid/uhid.c @@ -12,6 +12,7 @@ #include <linux/atomic.h> #include <linux/compat.h> +#include <linux/cred.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/hid.h> @@ -496,12 +497,13 @@ static int uhid_dev_create2(struct uhid_device *uhid, goto err_free; } - len = min(sizeof(hid->name), sizeof(ev->u.create2.name)); - strlcpy(hid->name, ev->u.create2.name, len); - len = min(sizeof(hid->phys), sizeof(ev->u.create2.phys)); - strlcpy(hid->phys, ev->u.create2.phys, len); - len = min(sizeof(hid->uniq), sizeof(ev->u.create2.uniq)); - strlcpy(hid->uniq, ev->u.create2.uniq, len); + /* @hid is zero-initialized, strncpy() is correct, strlcpy() not */ + len = min(sizeof(hid->name), sizeof(ev->u.create2.name)) - 1; + strncpy(hid->name, ev->u.create2.name, len); + len = min(sizeof(hid->phys), sizeof(ev->u.create2.phys)) - 1; + strncpy(hid->phys, ev->u.create2.phys, len); + len = min(sizeof(hid->uniq), sizeof(ev->u.create2.uniq)) - 1; + strncpy(hid->uniq, ev->u.create2.uniq, len); hid->ll_driver = &uhid_hid_driver; hid->bus = ev->u.create2.bus; @@ -722,6 +724,17 @@ static ssize_t uhid_char_write(struct file *file, const char __user *buffer, switch (uhid->input_buf.type) { case UHID_CREATE: + /* + * 'struct uhid_create_req' contains a __user pointer which is + * copied from, so it's unsafe to allow this with elevated + * privileges (e.g. from a setuid binary) or via kernel_write(). + */ + if (file->f_cred != current_cred() || uaccess_kernel()) { + pr_err_once("UHID_CREATE from different security context by process %d (%s), this is not allowed.\n", + task_tgid_vnr(current), current->comm); + ret = -EACCES; + goto unlock; + } ret = uhid_dev_create(uhid, &uhid->input_buf); break; case UHID_CREATE2: diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index a7513a8a8e37..d6106e1a0d4a 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -353,6 +353,9 @@ static void process_ib_ipinfo(void *in_msg, void *out_msg, int op) out->body.kvp_ip_val.dhcp_enabled = in->kvp_ip_val.dhcp_enabled; + /* fallthrough */ + + case KVP_OP_GET_IP_INFO: utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.adapter_id, MAX_ADAPTER_ID_SIZE, UTF16_LITTLE_ENDIAN, @@ -405,7 +408,11 @@ kvp_send_key(struct work_struct *dummy) process_ib_ipinfo(in_msg, message, KVP_OP_SET_IP_INFO); break; case KVP_OP_GET_IP_INFO: - /* We only need to pass on message->kvp_hdr.operation. */ + /* + * We only need to pass on the info of operation, adapter_id + * and addr_family to the userland kvp daemon. + */ + process_ib_ipinfo(in_msg, message, KVP_OP_GET_IP_INFO); break; case KVP_OP_SET: switch (in_msg->body.kvp_set.data.value_type) { @@ -446,9 +453,9 @@ kvp_send_key(struct work_struct *dummy) } - break; - - case KVP_OP_GET: + /* + * The key is always a string - utf16 encoding. + */ message->body.kvp_set.data.key_size = utf16s_to_utf8s( (wchar_t *)in_msg->body.kvp_set.data.key, @@ -456,6 +463,17 @@ kvp_send_key(struct work_struct *dummy) UTF16_LITTLE_ENDIAN, message->body.kvp_set.data.key, HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; + + break; + + case KVP_OP_GET: + message->body.kvp_get.data.key_size = + utf16s_to_utf8s( + (wchar_t *)in_msg->body.kvp_get.data.key, + in_msg->body.kvp_get.data.key_size, + UTF16_LITTLE_ENDIAN, + message->body.kvp_get.data.key, + HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; break; case KVP_OP_DELETE: diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index bb2cd29e1658..d8f7000a466a 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -797,7 +797,8 @@ static int iommu_init_ga_log(struct amd_iommu *iommu) entry = iommu_virt_to_phys(iommu->ga_log) | GA_LOG_SIZE_512; memcpy_toio(iommu->mmio_base + MMIO_GA_LOG_BASE_OFFSET, &entry, sizeof(entry)); - entry = (iommu_virt_to_phys(iommu->ga_log) & 0xFFFFFFFFFFFFFULL) & ~7ULL; + entry = (iommu_virt_to_phys(iommu->ga_log_tail) & + (BIT_ULL(52)-1)) & ~7ULL; memcpy_toio(iommu->mmio_base + MMIO_GA_LOG_TAIL_OFFSET, &entry, sizeof(entry)); writel(0x00, iommu->mmio_base + MMIO_GA_HEAD_OFFSET); diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index f3ccf025108b..41a4b8808802 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -3075,7 +3075,7 @@ static int copy_context_table(struct intel_iommu *iommu, } if (old_ce) - iounmap(old_ce); + memunmap(old_ce); ret = 0; if (devfn < 0x80) diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c index db301efe126d..887150907526 100644 --- a/drivers/iommu/intel-svm.c +++ b/drivers/iommu/intel-svm.c @@ -595,7 +595,7 @@ static irqreturn_t prq_event_thread(int irq, void *d) pr_err("%s: Page request without PASID: %08llx %08llx\n", iommu->name, ((unsigned long long *)req)[0], ((unsigned long long *)req)[1]); - goto bad_req; + goto no_pasid; } if (!svm || svm->pasid != req->pasid) { diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index b98a03189580..ddf3a492e1d5 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -498,6 +498,9 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain) static void ipmmu_domain_destroy_context(struct ipmmu_vmsa_domain *domain) { + if (!domain->mmu) + return; + /* * Disable the context. Flush the TLB as required when modifying the * context registers. diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index 31d1f4ab915e..65a933a21e68 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -807,7 +807,7 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, } if (adap->transmit_queue_sz >= CEC_MAX_MSG_TX_QUEUE_SZ) { - dprintk(1, "%s: transmit queue full\n", __func__); + dprintk(2, "%s: transmit queue full\n", __func__); return -EBUSY; } @@ -1180,6 +1180,8 @@ static int cec_config_log_addr(struct cec_adapter *adap, { struct cec_log_addrs *las = &adap->log_addrs; struct cec_msg msg = { }; + const unsigned int max_retries = 2; + unsigned int i; int err; if (cec_has_log_addr(adap, log_addr)) @@ -1188,19 +1190,44 @@ static int cec_config_log_addr(struct cec_adapter *adap, /* Send poll message */ msg.len = 1; msg.msg[0] = (log_addr << 4) | log_addr; - err = cec_transmit_msg_fh(adap, &msg, NULL, true); - /* - * While trying to poll the physical address was reset - * and the adapter was unconfigured, so bail out. - */ - if (!adap->is_configuring) - return -EINTR; + for (i = 0; i < max_retries; i++) { + err = cec_transmit_msg_fh(adap, &msg, NULL, true); - if (err) - return err; + /* + * While trying to poll the physical address was reset + * and the adapter was unconfigured, so bail out. + */ + if (!adap->is_configuring) + return -EINTR; + + if (err) + return err; - if (msg.tx_status & CEC_TX_STATUS_OK) + /* + * The message was aborted due to a disconnect or + * unconfigure, just bail out. + */ + if (msg.tx_status & CEC_TX_STATUS_ABORTED) + return -EINTR; + if (msg.tx_status & CEC_TX_STATUS_OK) + return 0; + if (msg.tx_status & CEC_TX_STATUS_NACK) + break; + /* + * Retry up to max_retries times if the message was neither + * OKed or NACKed. This can happen due to e.g. a Lost + * Arbitration condition. + */ + } + + /* + * If we are unable to get an OK or a NACK after max_retries attempts + * (and note that each attempt already consists of four polls), then + * then we assume that something is really weird and that it is not a + * good idea to try and claim this logical address. + */ + if (i == max_retries) return 0; /* diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index a38e54b8f687..22cafc07de25 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1918,7 +1918,6 @@ static int tc358743_probe_of(struct tc358743_state *state) ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint); if (ret) { dev_err(dev, "failed to parse endpoint\n"); - ret = ret; goto put_node; } diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c index 452eb9b42140..447baaebca44 100644 --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c @@ -1844,14 +1844,12 @@ fail_mutex_destroy: static void cio2_pci_remove(struct pci_dev *pci_dev) { struct cio2_device *cio2 = pci_get_drvdata(pci_dev); - unsigned int i; + media_device_unregister(&cio2->media_dev); cio2_notifier_exit(cio2); + cio2_queues_exit(cio2); cio2_fbpt_exit_dummy(cio2); - for (i = 0; i < CIO2_QUEUES; i++) - cio2_queue_exit(cio2, &cio2->queue[i]); v4l2_device_unregister(&cio2->v4l2_dev); - media_device_unregister(&cio2->media_dev); media_device_cleanup(&cio2->media_dev); mutex_destroy(&cio2->lock); } diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 77fb7987b42f..13f2828d880d 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -1587,6 +1587,8 @@ static void isp_pm_complete(struct device *dev) static void isp_unregister_entities(struct isp_device *isp) { + media_device_unregister(&isp->media_dev); + omap3isp_csi2_unregister_entities(&isp->isp_csi2a); omap3isp_ccp2_unregister_entities(&isp->isp_ccp2); omap3isp_ccdc_unregister_entities(&isp->isp_ccdc); @@ -1597,7 +1599,6 @@ static void isp_unregister_entities(struct isp_device *isp) omap3isp_stat_unregister_entities(&isp->isp_hist); v4l2_device_unregister(&isp->v4l2_dev); - media_device_unregister(&isp->media_dev); media_device_cleanup(&isp->media_dev); } diff --git a/drivers/media/platform/vicodec/vicodec-core.c b/drivers/media/platform/vicodec/vicodec-core.c index 1eb9132bfc85..b292cff26c86 100644 --- a/drivers/media/platform/vicodec/vicodec-core.c +++ b/drivers/media/platform/vicodec/vicodec-core.c @@ -42,7 +42,7 @@ MODULE_PARM_DESC(debug, " activates debug info"); #define MAX_WIDTH 4096U #define MIN_WIDTH 640U #define MAX_HEIGHT 2160U -#define MIN_HEIGHT 480U +#define MIN_HEIGHT 360U #define dprintk(dev, fmt, arg...) \ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg) diff --git a/drivers/media/platform/vim2m.c b/drivers/media/platform/vim2m.c index af150a0395df..d82db738f174 100644 --- a/drivers/media/platform/vim2m.c +++ b/drivers/media/platform/vim2m.c @@ -1009,7 +1009,7 @@ static const struct v4l2_m2m_ops m2m_ops = { static const struct media_device_ops m2m_media_ops = { .req_validate = vb2_request_validate, - .req_queue = vb2_m2m_request_queue, + .req_queue = v4l2_m2m_request_queue, }; static int vim2m_probe(struct platform_device *pdev) diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 6e37950292cd..5f2b033a7a42 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1664,6 +1664,11 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, p_mpeg2_slice_params->forward_ref_index >= VIDEO_MAX_FRAME) return -EINVAL; + if (p_mpeg2_slice_params->pad || + p_mpeg2_slice_params->picture.pad || + p_mpeg2_slice_params->sequence.pad) + return -EINVAL; + return 0; case V4L2_CTRL_TYPE_MPEG2_QUANTIZATION: diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c index a3ef1f50a4b3..481e3c65cf97 100644 --- a/drivers/media/v4l2-core/v4l2-event.c +++ b/drivers/media/v4l2-core/v4l2-event.c @@ -193,6 +193,22 @@ int v4l2_event_pending(struct v4l2_fh *fh) } EXPORT_SYMBOL_GPL(v4l2_event_pending); +static void __v4l2_event_unsubscribe(struct v4l2_subscribed_event *sev) +{ + struct v4l2_fh *fh = sev->fh; + unsigned int i; + + lockdep_assert_held(&fh->subscribe_lock); + assert_spin_locked(&fh->vdev->fh_lock); + + /* Remove any pending events for this subscription */ + for (i = 0; i < sev->in_use; i++) { + list_del(&sev->events[sev_pos(sev, i)].list); + fh->navailable--; + } + list_del(&sev->list); +} + int v4l2_event_subscribe(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub, unsigned elems, const struct v4l2_subscribed_event_ops *ops) @@ -224,27 +240,23 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, spin_lock_irqsave(&fh->vdev->fh_lock, flags); found_ev = v4l2_event_subscribed(fh, sub->type, sub->id); + if (!found_ev) + list_add(&sev->list, &fh->subscribed); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); if (found_ev) { /* Already listening */ kvfree(sev); - goto out_unlock; - } - - if (sev->ops && sev->ops->add) { + } else if (sev->ops && sev->ops->add) { ret = sev->ops->add(sev, elems); if (ret) { + spin_lock_irqsave(&fh->vdev->fh_lock, flags); + __v4l2_event_unsubscribe(sev); + spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); kvfree(sev); - goto out_unlock; } } - spin_lock_irqsave(&fh->vdev->fh_lock, flags); - list_add(&sev->list, &fh->subscribed); - spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - -out_unlock: mutex_unlock(&fh->subscribe_lock); return ret; @@ -279,7 +291,6 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh, { struct v4l2_subscribed_event *sev; unsigned long flags; - int i; if (sub->type == V4L2_EVENT_ALL) { v4l2_event_unsubscribe_all(fh); @@ -291,14 +302,8 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh, spin_lock_irqsave(&fh->vdev->fh_lock, flags); sev = v4l2_event_subscribed(fh, sub->type, sub->id); - if (sev != NULL) { - /* Remove any pending events for this subscription */ - for (i = 0; i < sev->in_use; i++) { - list_del(&sev->events[sev_pos(sev, i)].list); - fh->navailable--; - } - list_del(&sev->list); - } + if (sev != NULL) + __v4l2_event_unsubscribe(sev); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index d7806db222d8..1ed2465972ac 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -953,7 +953,7 @@ void v4l2_m2m_buf_queue(struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_buf_queue); -void vb2_m2m_request_queue(struct media_request *req) +void v4l2_m2m_request_queue(struct media_request *req) { struct media_request_object *obj, *obj_safe; struct v4l2_m2m_ctx *m2m_ctx = NULL; @@ -997,7 +997,7 @@ void vb2_m2m_request_queue(struct media_request *req) if (m2m_ctx) v4l2_m2m_try_schedule(m2m_ctx); } -EXPORT_SYMBOL_GPL(vb2_m2m_request_queue); +EXPORT_SYMBOL_GPL(v4l2_m2m_request_queue); /* Videobuf2 ioctl helpers */ diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index b2a0340f277e..d8e3cc2dc747 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -132,7 +132,7 @@ static const struct of_device_id atmel_ssc_dt_ids[] = { MODULE_DEVICE_TABLE(of, atmel_ssc_dt_ids); #endif -static inline const struct atmel_ssc_platform_data * __init +static inline const struct atmel_ssc_platform_data * atmel_ssc_get_driver_data(struct platform_device *pdev) { if (pdev->dev.of_node) { diff --git a/drivers/misc/sgi-gru/grukdump.c b/drivers/misc/sgi-gru/grukdump.c index 313da3150262..1540a7785e14 100644 --- a/drivers/misc/sgi-gru/grukdump.c +++ b/drivers/misc/sgi-gru/grukdump.c @@ -27,6 +27,9 @@ #include <linux/delay.h> #include <linux/bitops.h> #include <asm/uv/uv_hub.h> + +#include <linux/nospec.h> + #include "gru.h" #include "grutables.h" #include "gruhandles.h" @@ -196,6 +199,7 @@ int gru_dump_chiplet_request(unsigned long arg) /* Currently, only dump by gid is implemented */ if (req.gid >= gru_max_gids) return -EINVAL; + req.gid = array_index_nospec(req.gid, gru_max_gids); gru = GID_TO_GRU(req.gid); ubuf = req.buf; diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 7bfd366d970d..c4115bae5db1 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -12,6 +12,7 @@ * - JMicron (hardware and technical support) */ +#include <linux/bitfield.h> #include <linux/string.h> #include <linux/delay.h> #include <linux/highmem.h> @@ -462,6 +463,9 @@ struct intel_host { u32 dsm_fns; int drv_strength; bool d3_retune; + bool rpm_retune_ok; + u32 glk_rx_ctrl1; + u32 glk_tun_val; }; static const guid_t intel_dsm_guid = @@ -791,6 +795,77 @@ cleanup: return ret; } +#ifdef CONFIG_PM +#define GLK_RX_CTRL1 0x834 +#define GLK_TUN_VAL 0x840 +#define GLK_PATH_PLL GENMASK(13, 8) +#define GLK_DLY GENMASK(6, 0) +/* Workaround firmware failing to restore the tuning value */ +static void glk_rpm_retune_wa(struct sdhci_pci_chip *chip, bool susp) +{ + struct sdhci_pci_slot *slot = chip->slots[0]; + struct intel_host *intel_host = sdhci_pci_priv(slot); + struct sdhci_host *host = slot->host; + u32 glk_rx_ctrl1; + u32 glk_tun_val; + u32 dly; + + if (intel_host->rpm_retune_ok || !mmc_can_retune(host->mmc)) + return; + + glk_rx_ctrl1 = sdhci_readl(host, GLK_RX_CTRL1); + glk_tun_val = sdhci_readl(host, GLK_TUN_VAL); + + if (susp) { + intel_host->glk_rx_ctrl1 = glk_rx_ctrl1; + intel_host->glk_tun_val = glk_tun_val; + return; + } + + if (!intel_host->glk_tun_val) + return; + + if (glk_rx_ctrl1 != intel_host->glk_rx_ctrl1) { + intel_host->rpm_retune_ok = true; + return; + } + + dly = FIELD_PREP(GLK_DLY, FIELD_GET(GLK_PATH_PLL, glk_rx_ctrl1) + + (intel_host->glk_tun_val << 1)); + if (dly == FIELD_GET(GLK_DLY, glk_rx_ctrl1)) + return; + + glk_rx_ctrl1 = (glk_rx_ctrl1 & ~GLK_DLY) | dly; + sdhci_writel(host, glk_rx_ctrl1, GLK_RX_CTRL1); + + intel_host->rpm_retune_ok = true; + chip->rpm_retune = true; + mmc_retune_needed(host->mmc); + pr_info("%s: Requiring re-tune after rpm resume", mmc_hostname(host->mmc)); +} + +static void glk_rpm_retune_chk(struct sdhci_pci_chip *chip, bool susp) +{ + if (chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_EMMC && + !chip->rpm_retune) + glk_rpm_retune_wa(chip, susp); +} + +static int glk_runtime_suspend(struct sdhci_pci_chip *chip) +{ + glk_rpm_retune_chk(chip, true); + + return sdhci_cqhci_runtime_suspend(chip); +} + +static int glk_runtime_resume(struct sdhci_pci_chip *chip) +{ + glk_rpm_retune_chk(chip, false); + + return sdhci_cqhci_runtime_resume(chip); +} +#endif + #ifdef CONFIG_ACPI static int ni_set_max_freq(struct sdhci_pci_slot *slot) { @@ -879,8 +954,8 @@ static const struct sdhci_pci_fixes sdhci_intel_glk_emmc = { .resume = sdhci_cqhci_resume, #endif #ifdef CONFIG_PM - .runtime_suspend = sdhci_cqhci_runtime_suspend, - .runtime_resume = sdhci_cqhci_runtime_resume, + .runtime_suspend = glk_runtime_suspend, + .runtime_resume = glk_runtime_resume, #endif .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | @@ -1762,8 +1837,13 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( device_init_wakeup(&pdev->dev, true); if (slot->cd_idx >= 0) { - ret = mmc_gpiod_request_cd(host->mmc, NULL, slot->cd_idx, + ret = mmc_gpiod_request_cd(host->mmc, "cd", slot->cd_idx, slot->cd_override_level, 0, NULL); + if (ret && ret != -EPROBE_DEFER) + ret = mmc_gpiod_request_cd(host->mmc, NULL, + slot->cd_idx, + slot->cd_override_level, + 0, NULL); if (ret == -EPROBE_DEFER) goto remove; diff --git a/drivers/mtd/nand/raw/atmel/nand-controller.c b/drivers/mtd/nand/raw/atmel/nand-controller.c index fb33f6be7c4f..ad720494e8f7 100644 --- a/drivers/mtd/nand/raw/atmel/nand-controller.c +++ b/drivers/mtd/nand/raw/atmel/nand-controller.c @@ -2032,8 +2032,7 @@ atmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc) int ret; nand_np = dev->of_node; - nfc_np = of_find_compatible_node(dev->of_node, NULL, - "atmel,sama5d3-nfc"); + nfc_np = of_get_compatible_child(dev->of_node, "atmel,sama5d3-nfc"); if (!nfc_np) { dev_err(dev, "Could not find device node for sama5d3-nfc\n"); return -ENODEV; @@ -2447,15 +2446,19 @@ static int atmel_nand_controller_probe(struct platform_device *pdev) } if (caps->legacy_of_bindings) { + struct device_node *nfc_node; u32 ale_offs = 21; /* * If we are parsing legacy DT props and the DT contains a * valid NFC node, forward the request to the sama5 logic. */ - if (of_find_compatible_node(pdev->dev.of_node, NULL, - "atmel,sama5d3-nfc")) + nfc_node = of_get_compatible_child(pdev->dev.of_node, + "atmel,sama5d3-nfc"); + if (nfc_node) { caps = &atmel_sama5_nand_caps; + of_node_put(nfc_node); + } /* * Even if the compatible says we are dealing with an diff --git a/drivers/mtd/nand/raw/qcom_nandc.c b/drivers/mtd/nand/raw/qcom_nandc.c index ef75dfa62a4f..699d3cf49c6d 100644 --- a/drivers/mtd/nand/raw/qcom_nandc.c +++ b/drivers/mtd/nand/raw/qcom_nandc.c @@ -150,15 +150,15 @@ #define NAND_VERSION_MINOR_SHIFT 16 /* NAND OP_CMDs */ -#define PAGE_READ 0x2 -#define PAGE_READ_WITH_ECC 0x3 -#define PAGE_READ_WITH_ECC_SPARE 0x4 -#define PROGRAM_PAGE 0x6 -#define PAGE_PROGRAM_WITH_ECC 0x7 -#define PROGRAM_PAGE_SPARE 0x9 -#define BLOCK_ERASE 0xa -#define FETCH_ID 0xb -#define RESET_DEVICE 0xd +#define OP_PAGE_READ 0x2 +#define OP_PAGE_READ_WITH_ECC 0x3 +#define OP_PAGE_READ_WITH_ECC_SPARE 0x4 +#define OP_PROGRAM_PAGE 0x6 +#define OP_PAGE_PROGRAM_WITH_ECC 0x7 +#define OP_PROGRAM_PAGE_SPARE 0x9 +#define OP_BLOCK_ERASE 0xa +#define OP_FETCH_ID 0xb +#define OP_RESET_DEVICE 0xd /* Default Value for NAND_DEV_CMD_VLD */ #define NAND_DEV_CMD_VLD_VAL (READ_START_VLD | WRITE_START_VLD | \ @@ -692,11 +692,11 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read) if (read) { if (host->use_ecc) - cmd = PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE; + cmd = OP_PAGE_READ_WITH_ECC | PAGE_ACC | LAST_PAGE; else - cmd = PAGE_READ | PAGE_ACC | LAST_PAGE; + cmd = OP_PAGE_READ | PAGE_ACC | LAST_PAGE; } else { - cmd = PROGRAM_PAGE | PAGE_ACC | LAST_PAGE; + cmd = OP_PROGRAM_PAGE | PAGE_ACC | LAST_PAGE; } if (host->use_ecc) { @@ -1170,7 +1170,7 @@ static int nandc_param(struct qcom_nand_host *host) * in use. we configure the controller to perform a raw read of 512 * bytes to read onfi params */ - nandc_set_reg(nandc, NAND_FLASH_CMD, PAGE_READ | PAGE_ACC | LAST_PAGE); + nandc_set_reg(nandc, NAND_FLASH_CMD, OP_PAGE_READ | PAGE_ACC | LAST_PAGE); nandc_set_reg(nandc, NAND_ADDR0, 0); nandc_set_reg(nandc, NAND_ADDR1, 0); nandc_set_reg(nandc, NAND_DEV0_CFG0, 0 << CW_PER_PAGE @@ -1224,7 +1224,7 @@ static int erase_block(struct qcom_nand_host *host, int page_addr) struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); nandc_set_reg(nandc, NAND_FLASH_CMD, - BLOCK_ERASE | PAGE_ACC | LAST_PAGE); + OP_BLOCK_ERASE | PAGE_ACC | LAST_PAGE); nandc_set_reg(nandc, NAND_ADDR0, page_addr); nandc_set_reg(nandc, NAND_ADDR1, 0); nandc_set_reg(nandc, NAND_DEV0_CFG0, @@ -1255,7 +1255,7 @@ static int read_id(struct qcom_nand_host *host, int column) if (column == -1) return 0; - nandc_set_reg(nandc, NAND_FLASH_CMD, FETCH_ID); + nandc_set_reg(nandc, NAND_FLASH_CMD, OP_FETCH_ID); nandc_set_reg(nandc, NAND_ADDR0, column); nandc_set_reg(nandc, NAND_ADDR1, 0); nandc_set_reg(nandc, NAND_FLASH_CHIP_SELECT, @@ -1276,7 +1276,7 @@ static int reset(struct qcom_nand_host *host) struct nand_chip *chip = &host->chip; struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip); - nandc_set_reg(nandc, NAND_FLASH_CMD, RESET_DEVICE); + nandc_set_reg(nandc, NAND_FLASH_CMD, OP_RESET_DEVICE); nandc_set_reg(nandc, NAND_EXEC_CMD, 1); write_reg_dma(nandc, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL); diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c index d846428ef038..04cedd3a2bf6 100644 --- a/drivers/mtd/spi-nor/cadence-quadspi.c +++ b/drivers/mtd/spi-nor/cadence-quadspi.c @@ -644,9 +644,23 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor, loff_t to_addr, ndelay(cqspi->wr_delay); while (remaining > 0) { + size_t write_words, mod_bytes; + write_bytes = remaining > page_size ? page_size : remaining; - iowrite32_rep(cqspi->ahb_base, txbuf, - DIV_ROUND_UP(write_bytes, 4)); + write_words = write_bytes / 4; + mod_bytes = write_bytes % 4; + /* Write 4 bytes at a time then single bytes. */ + if (write_words) { + iowrite32_rep(cqspi->ahb_base, txbuf, write_words); + txbuf += (write_words * 4); + } + if (mod_bytes) { + unsigned int temp = 0xFFFFFFFF; + + memcpy(&temp, txbuf, mod_bytes); + iowrite32(temp, cqspi->ahb_base); + txbuf += mod_bytes; + } if (!wait_for_completion_timeout(&cqspi->transfer_complete, msecs_to_jiffies(CQSPI_TIMEOUT_MS))) { @@ -655,7 +669,6 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor, loff_t to_addr, goto failwr; } - txbuf += write_bytes; remaining -= write_bytes; if (remaining > 0) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 3e54e31889c7..93c9bc8931fc 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -2156,7 +2156,7 @@ spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, * @nor: pointer to a 'struct spi_nor' * @addr: offset in the serial flash memory * @len: number of bytes to read - * @buf: buffer where the data is copied into + * @buf: buffer where the data is copied into (dma-safe memory) * * Return: 0 on success, -errno otherwise. */ @@ -2522,6 +2522,34 @@ static int spi_nor_map_cmp_erase_type(const void *l, const void *r) } /** + * spi_nor_sort_erase_mask() - sort erase mask + * @map: the erase map of the SPI NOR + * @erase_mask: the erase type mask to be sorted + * + * Replicate the sort done for the map's erase types in BFPT: sort the erase + * mask in ascending order with the smallest erase type size starting from + * BIT(0) in the sorted erase mask. + * + * Return: sorted erase mask. + */ +static u8 spi_nor_sort_erase_mask(struct spi_nor_erase_map *map, u8 erase_mask) +{ + struct spi_nor_erase_type *erase_type = map->erase_type; + int i; + u8 sorted_erase_mask = 0; + + if (!erase_mask) + return 0; + + /* Replicate the sort done for the map's erase types. */ + for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) + if (erase_type[i].size && erase_mask & BIT(erase_type[i].idx)) + sorted_erase_mask |= BIT(i); + + return sorted_erase_mask; +} + +/** * spi_nor_regions_sort_erase_types() - sort erase types in each region * @map: the erase map of the SPI NOR * @@ -2536,19 +2564,13 @@ static int spi_nor_map_cmp_erase_type(const void *l, const void *r) static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map) { struct spi_nor_erase_region *region = map->regions; - struct spi_nor_erase_type *erase_type = map->erase_type; - int i; u8 region_erase_mask, sorted_erase_mask; while (region) { region_erase_mask = region->offset & SNOR_ERASE_TYPE_MASK; - /* Replicate the sort done for the map's erase types. */ - sorted_erase_mask = 0; - for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) - if (erase_type[i].size && - region_erase_mask & BIT(erase_type[i].idx)) - sorted_erase_mask |= BIT(i); + sorted_erase_mask = spi_nor_sort_erase_mask(map, + region_erase_mask); /* Overwrite erase mask. */ region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK) | @@ -2855,52 +2877,84 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings) * spi_nor_get_map_in_use() - get the configuration map in use * @nor: pointer to a 'struct spi_nor' * @smpt: pointer to the sector map parameter table + * @smpt_len: sector map parameter table length + * + * Return: pointer to the map in use, ERR_PTR(-errno) otherwise. */ -static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt) +static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt, + u8 smpt_len) { - const u32 *ret = NULL; - u32 i, addr; + const u32 *ret; + u8 *buf; + u32 addr; int err; + u8 i; u8 addr_width, read_opcode, read_dummy; - u8 read_data_mask, data_byte, map_id; + u8 read_data_mask, map_id; + + /* Use a kmalloc'ed bounce buffer to guarantee it is DMA-able. */ + buf = kmalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); addr_width = nor->addr_width; read_dummy = nor->read_dummy; read_opcode = nor->read_opcode; map_id = 0; - i = 0; /* Determine if there are any optional Detection Command Descriptors */ - while (!(smpt[i] & SMPT_DESC_TYPE_MAP)) { + for (i = 0; i < smpt_len; i += 2) { + if (smpt[i] & SMPT_DESC_TYPE_MAP) + break; + read_data_mask = SMPT_CMD_READ_DATA(smpt[i]); nor->addr_width = spi_nor_smpt_addr_width(nor, smpt[i]); nor->read_dummy = spi_nor_smpt_read_dummy(nor, smpt[i]); nor->read_opcode = SMPT_CMD_OPCODE(smpt[i]); addr = smpt[i + 1]; - err = spi_nor_read_raw(nor, addr, 1, &data_byte); - if (err) + err = spi_nor_read_raw(nor, addr, 1, buf); + if (err) { + ret = ERR_PTR(err); goto out; + } /* * Build an index value that is used to select the Sector Map * Configuration that is currently in use. */ - map_id = map_id << 1 | !!(data_byte & read_data_mask); - i = i + 2; + map_id = map_id << 1 | !!(*buf & read_data_mask); } - /* Find the matching configuration map */ - while (SMPT_MAP_ID(smpt[i]) != map_id) { + /* + * If command descriptors are provided, they always precede map + * descriptors in the table. There is no need to start the iteration + * over smpt array all over again. + * + * Find the matching configuration map. + */ + ret = ERR_PTR(-EINVAL); + while (i < smpt_len) { + if (SMPT_MAP_ID(smpt[i]) == map_id) { + ret = smpt + i; + break; + } + + /* + * If there are no more configuration map descriptors and no + * configuration ID matched the configuration identifier, the + * sector address map is unknown. + */ if (smpt[i] & SMPT_DESC_END) - goto out; + break; + /* increment the table index to the next map */ i += SMPT_MAP_REGION_COUNT(smpt[i]) + 1; } - ret = smpt + i; /* fall through */ out: + kfree(buf); nor->addr_width = addr_width; nor->read_dummy = read_dummy; nor->read_opcode = read_opcode; @@ -2946,7 +3000,7 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, u64 offset; u32 region_count; int i, j; - u8 erase_type; + u8 erase_type, uniform_erase_type; region_count = SMPT_MAP_REGION_COUNT(*smpt); /* @@ -2959,7 +3013,7 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, return -ENOMEM; map->regions = region; - map->uniform_erase_type = 0xff; + uniform_erase_type = 0xff; offset = 0; /* Populate regions. */ for (i = 0; i < region_count; i++) { @@ -2974,12 +3028,15 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor, * Save the erase types that are supported in all regions and * can erase the entire flash memory. */ - map->uniform_erase_type &= erase_type; + uniform_erase_type &= erase_type; offset = (region[i].offset & ~SNOR_ERASE_FLAGS_MASK) + region[i].size; } + map->uniform_erase_type = spi_nor_sort_erase_mask(map, + uniform_erase_type); + spi_nor_region_mark_end(®ion[i - 1]); return 0; @@ -3020,9 +3077,9 @@ static int spi_nor_parse_smpt(struct spi_nor *nor, for (i = 0; i < smpt_header->length; i++) smpt[i] = le32_to_cpu(smpt[i]); - sector_map = spi_nor_get_map_in_use(nor, smpt); - if (!sector_map) { - ret = -EINVAL; + sector_map = spi_nor_get_map_in_use(nor, smpt, smpt_header->length); + if (IS_ERR(sector_map)) { + ret = PTR_ERR(sector_map); goto out; } @@ -3125,7 +3182,7 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor, if (err) goto exit; - /* Parse other parameter headers. */ + /* Parse optional parameter tables. */ for (i = 0; i < header.nph; i++) { param_header = ¶m_headers[i]; @@ -3138,8 +3195,17 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor, break; } - if (err) - goto exit; + if (err) { + dev_warn(dev, "Failed to parse optional parameter table: %04x\n", + SFDP_PARAM_HEADER_ID(param_header)); + /* + * Let's not drop all information we extracted so far + * if optional table parsers fail. In case of failing, + * each optional parser is responsible to roll back to + * the previously known spi_nor data. + */ + err = 0; + } } exit: diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 49163570a63a..3b3f88ffab53 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -477,6 +477,34 @@ void can_put_echo_skb(struct sk_buff *skb, struct net_device *dev, } EXPORT_SYMBOL_GPL(can_put_echo_skb); +struct sk_buff *__can_get_echo_skb(struct net_device *dev, unsigned int idx, u8 *len_ptr) +{ + struct can_priv *priv = netdev_priv(dev); + struct sk_buff *skb = priv->echo_skb[idx]; + struct canfd_frame *cf; + + if (idx >= priv->echo_skb_max) { + netdev_err(dev, "%s: BUG! Trying to access can_priv::echo_skb out of bounds (%u/max %u)\n", + __func__, idx, priv->echo_skb_max); + return NULL; + } + + if (!skb) { + netdev_err(dev, "%s: BUG! Trying to echo non existing skb: can_priv::echo_skb[%u]\n", + __func__, idx); + return NULL; + } + + /* Using "struct canfd_frame::len" for the frame + * length is supported on both CAN and CANFD frames. + */ + cf = (struct canfd_frame *)skb->data; + *len_ptr = cf->len; + priv->echo_skb[idx] = NULL; + + return skb; +} + /* * Get the skb from the stack and loop it back locally * @@ -486,22 +514,16 @@ EXPORT_SYMBOL_GPL(can_put_echo_skb); */ unsigned int can_get_echo_skb(struct net_device *dev, unsigned int idx) { - struct can_priv *priv = netdev_priv(dev); - - BUG_ON(idx >= priv->echo_skb_max); - - if (priv->echo_skb[idx]) { - struct sk_buff *skb = priv->echo_skb[idx]; - struct can_frame *cf = (struct can_frame *)skb->data; - u8 dlc = cf->can_dlc; + struct sk_buff *skb; + u8 len; - netif_rx(priv->echo_skb[idx]); - priv->echo_skb[idx] = NULL; + skb = __can_get_echo_skb(dev, idx, &len); + if (!skb) + return 0; - return dlc; - } + netif_rx(skb); - return 0; + return len; } EXPORT_SYMBOL_GPL(can_get_echo_skb); diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 8e972ef08637..75ce11395ee8 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -135,13 +135,12 @@ /* FLEXCAN interrupt flag register (IFLAG) bits */ /* Errata ERR005829 step7: Reserve first valid MB */ -#define FLEXCAN_TX_MB_RESERVED_OFF_FIFO 8 -#define FLEXCAN_TX_MB_OFF_FIFO 9 +#define FLEXCAN_TX_MB_RESERVED_OFF_FIFO 8 #define FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP 0 -#define FLEXCAN_TX_MB_OFF_TIMESTAMP 1 -#define FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST (FLEXCAN_TX_MB_OFF_TIMESTAMP + 1) -#define FLEXCAN_RX_MB_OFF_TIMESTAMP_LAST 63 -#define FLEXCAN_IFLAG_MB(x) BIT(x) +#define FLEXCAN_TX_MB 63 +#define FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST (FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP + 1) +#define FLEXCAN_RX_MB_OFF_TIMESTAMP_LAST (FLEXCAN_TX_MB - 1) +#define FLEXCAN_IFLAG_MB(x) BIT(x & 0x1f) #define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW BIT(7) #define FLEXCAN_IFLAG_RX_FIFO_WARN BIT(6) #define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE BIT(5) @@ -259,9 +258,7 @@ struct flexcan_priv { struct can_rx_offload offload; struct flexcan_regs __iomem *regs; - struct flexcan_mb __iomem *tx_mb; struct flexcan_mb __iomem *tx_mb_reserved; - u8 tx_mb_idx; u32 reg_ctrl_default; u32 reg_imask1_default; u32 reg_imask2_default; @@ -515,6 +512,7 @@ static int flexcan_get_berr_counter(const struct net_device *dev, static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *dev) { const struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->regs; struct can_frame *cf = (struct can_frame *)skb->data; u32 can_id; u32 data; @@ -537,17 +535,17 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de if (cf->can_dlc > 0) { data = be32_to_cpup((__be32 *)&cf->data[0]); - priv->write(data, &priv->tx_mb->data[0]); + priv->write(data, ®s->mb[FLEXCAN_TX_MB].data[0]); } if (cf->can_dlc > 4) { data = be32_to_cpup((__be32 *)&cf->data[4]); - priv->write(data, &priv->tx_mb->data[1]); + priv->write(data, ®s->mb[FLEXCAN_TX_MB].data[1]); } can_put_echo_skb(skb, dev, 0); - priv->write(can_id, &priv->tx_mb->can_id); - priv->write(ctrl, &priv->tx_mb->can_ctrl); + priv->write(can_id, ®s->mb[FLEXCAN_TX_MB].can_id); + priv->write(ctrl, ®s->mb[FLEXCAN_TX_MB].can_ctrl); /* Errata ERR005829 step8: * Write twice INACTIVE(0x8) code to first MB. @@ -563,9 +561,13 @@ static netdev_tx_t flexcan_start_xmit(struct sk_buff *skb, struct net_device *de static void flexcan_irq_bus_err(struct net_device *dev, u32 reg_esr) { struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->regs; struct sk_buff *skb; struct can_frame *cf; bool rx_errors = false, tx_errors = false; + u32 timestamp; + + timestamp = priv->read(®s->timer) << 16; skb = alloc_can_err_skb(dev, &cf); if (unlikely(!skb)) @@ -612,17 +614,21 @@ static void flexcan_irq_bus_err(struct net_device *dev, u32 reg_esr) if (tx_errors) dev->stats.tx_errors++; - can_rx_offload_irq_queue_err_skb(&priv->offload, skb); + can_rx_offload_queue_sorted(&priv->offload, skb, timestamp); } static void flexcan_irq_state(struct net_device *dev, u32 reg_esr) { struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_regs __iomem *regs = priv->regs; struct sk_buff *skb; struct can_frame *cf; enum can_state new_state, rx_state, tx_state; int flt; struct can_berr_counter bec; + u32 timestamp; + + timestamp = priv->read(®s->timer) << 16; flt = reg_esr & FLEXCAN_ESR_FLT_CONF_MASK; if (likely(flt == FLEXCAN_ESR_FLT_CONF_ACTIVE)) { @@ -652,7 +658,7 @@ static void flexcan_irq_state(struct net_device *dev, u32 reg_esr) if (unlikely(new_state == CAN_STATE_BUS_OFF)) can_bus_off(dev); - can_rx_offload_irq_queue_err_skb(&priv->offload, skb); + can_rx_offload_queue_sorted(&priv->offload, skb, timestamp); } static inline struct flexcan_priv *rx_offload_to_priv(struct can_rx_offload *offload) @@ -720,9 +726,14 @@ static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload, priv->write(BIT(n - 32), ®s->iflag2); } else { priv->write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->iflag1); - priv->read(®s->timer); } + /* Read the Free Running Timer. It is optional but recommended + * to unlock Mailbox as soon as possible and make it available + * for reception. + */ + priv->read(®s->timer); + return 1; } @@ -732,9 +743,9 @@ static inline u64 flexcan_read_reg_iflag_rx(struct flexcan_priv *priv) struct flexcan_regs __iomem *regs = priv->regs; u32 iflag1, iflag2; - iflag2 = priv->read(®s->iflag2) & priv->reg_imask2_default; - iflag1 = priv->read(®s->iflag1) & priv->reg_imask1_default & - ~FLEXCAN_IFLAG_MB(priv->tx_mb_idx); + iflag2 = priv->read(®s->iflag2) & priv->reg_imask2_default & + ~FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB); + iflag1 = priv->read(®s->iflag1) & priv->reg_imask1_default; return (u64)iflag2 << 32 | iflag1; } @@ -746,11 +757,9 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) struct flexcan_priv *priv = netdev_priv(dev); struct flexcan_regs __iomem *regs = priv->regs; irqreturn_t handled = IRQ_NONE; - u32 reg_iflag1, reg_esr; + u32 reg_iflag2, reg_esr; enum can_state last_state = priv->can.state; - reg_iflag1 = priv->read(®s->iflag1); - /* reception interrupt */ if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) { u64 reg_iflag; @@ -764,6 +773,9 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) break; } } else { + u32 reg_iflag1; + + reg_iflag1 = priv->read(®s->iflag1); if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) { handled = IRQ_HANDLED; can_rx_offload_irq_offload_fifo(&priv->offload); @@ -779,17 +791,22 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) } } + reg_iflag2 = priv->read(®s->iflag2); + /* transmission complete interrupt */ - if (reg_iflag1 & FLEXCAN_IFLAG_MB(priv->tx_mb_idx)) { + if (reg_iflag2 & FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB)) { + u32 reg_ctrl = priv->read(®s->mb[FLEXCAN_TX_MB].can_ctrl); + handled = IRQ_HANDLED; - stats->tx_bytes += can_get_echo_skb(dev, 0); + stats->tx_bytes += can_rx_offload_get_echo_skb(&priv->offload, + 0, reg_ctrl << 16); stats->tx_packets++; can_led_event(dev, CAN_LED_EVENT_TX); /* after sending a RTR frame MB is in RX mode */ priv->write(FLEXCAN_MB_CODE_TX_INACTIVE, - &priv->tx_mb->can_ctrl); - priv->write(FLEXCAN_IFLAG_MB(priv->tx_mb_idx), ®s->iflag1); + ®s->mb[FLEXCAN_TX_MB].can_ctrl); + priv->write(FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB), ®s->iflag2); netif_wake_queue(dev); } @@ -931,15 +948,13 @@ static int flexcan_chip_start(struct net_device *dev) reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff); reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_IRMQ | - FLEXCAN_MCR_IDAM_C; + FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_MAXMB(FLEXCAN_TX_MB); - if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) { + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) reg_mcr &= ~FLEXCAN_MCR_FEN; - reg_mcr |= FLEXCAN_MCR_MAXMB(priv->offload.mb_last); - } else { - reg_mcr |= FLEXCAN_MCR_FEN | - FLEXCAN_MCR_MAXMB(priv->tx_mb_idx); - } + else + reg_mcr |= FLEXCAN_MCR_FEN; + netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr); priv->write(reg_mcr, ®s->mcr); @@ -982,16 +997,17 @@ static int flexcan_chip_start(struct net_device *dev) priv->write(reg_ctrl2, ®s->ctrl2); } - /* clear and invalidate all mailboxes first */ - for (i = priv->tx_mb_idx; i < ARRAY_SIZE(regs->mb); i++) { - priv->write(FLEXCAN_MB_CODE_RX_INACTIVE, - ®s->mb[i].can_ctrl); - } - if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) { - for (i = priv->offload.mb_first; i <= priv->offload.mb_last; i++) + for (i = priv->offload.mb_first; i <= priv->offload.mb_last; i++) { priv->write(FLEXCAN_MB_CODE_RX_EMPTY, ®s->mb[i].can_ctrl); + } + } else { + /* clear and invalidate unused mailboxes first */ + for (i = FLEXCAN_TX_MB_RESERVED_OFF_FIFO; i <= ARRAY_SIZE(regs->mb); i++) { + priv->write(FLEXCAN_MB_CODE_RX_INACTIVE, + ®s->mb[i].can_ctrl); + } } /* Errata ERR005829: mark first TX mailbox as INACTIVE */ @@ -1000,7 +1016,7 @@ static int flexcan_chip_start(struct net_device *dev) /* mark TX mailbox as INACTIVE */ priv->write(FLEXCAN_MB_CODE_TX_INACTIVE, - &priv->tx_mb->can_ctrl); + ®s->mb[FLEXCAN_TX_MB].can_ctrl); /* acceptance mask/acceptance code (accept everything) */ priv->write(0x0, ®s->rxgmask); @@ -1355,17 +1371,13 @@ static int flexcan_probe(struct platform_device *pdev) priv->devtype_data = devtype_data; priv->reg_xceiver = reg_xceiver; - if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) { - priv->tx_mb_idx = FLEXCAN_TX_MB_OFF_TIMESTAMP; + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) priv->tx_mb_reserved = ®s->mb[FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP]; - } else { - priv->tx_mb_idx = FLEXCAN_TX_MB_OFF_FIFO; + else priv->tx_mb_reserved = ®s->mb[FLEXCAN_TX_MB_RESERVED_OFF_FIFO]; - } - priv->tx_mb = ®s->mb[priv->tx_mb_idx]; - priv->reg_imask1_default = FLEXCAN_IFLAG_MB(priv->tx_mb_idx); - priv->reg_imask2_default = 0; + priv->reg_imask1_default = 0; + priv->reg_imask2_default = FLEXCAN_IFLAG_MB(FLEXCAN_TX_MB); priv->offload.mailbox_read = flexcan_mailbox_read; diff --git a/drivers/net/can/rcar/rcar_can.c b/drivers/net/can/rcar/rcar_can.c index 11662f479e76..771a46083739 100644 --- a/drivers/net/can/rcar/rcar_can.c +++ b/drivers/net/can/rcar/rcar_can.c @@ -24,6 +24,9 @@ #define RCAR_CAN_DRV_NAME "rcar_can" +#define RCAR_SUPPORTED_CLOCKS (BIT(CLKR_CLKP1) | BIT(CLKR_CLKP2) | \ + BIT(CLKR_CLKEXT)) + /* Mailbox configuration: * mailbox 60 - 63 - Rx FIFO mailboxes * mailbox 56 - 59 - Tx FIFO mailboxes @@ -789,7 +792,7 @@ static int rcar_can_probe(struct platform_device *pdev) goto fail_clk; } - if (clock_select >= ARRAY_SIZE(clock_names)) { + if (!(BIT(clock_select) & RCAR_SUPPORTED_CLOCKS)) { err = -EINVAL; dev_err(&pdev->dev, "invalid CAN clock selected\n"); goto fail_clk; diff --git a/drivers/net/can/rx-offload.c b/drivers/net/can/rx-offload.c index c7d05027a7a0..2ce4fa8698c7 100644 --- a/drivers/net/can/rx-offload.c +++ b/drivers/net/can/rx-offload.c @@ -211,7 +211,54 @@ int can_rx_offload_irq_offload_fifo(struct can_rx_offload *offload) } EXPORT_SYMBOL_GPL(can_rx_offload_irq_offload_fifo); -int can_rx_offload_irq_queue_err_skb(struct can_rx_offload *offload, struct sk_buff *skb) +int can_rx_offload_queue_sorted(struct can_rx_offload *offload, + struct sk_buff *skb, u32 timestamp) +{ + struct can_rx_offload_cb *cb; + unsigned long flags; + + if (skb_queue_len(&offload->skb_queue) > + offload->skb_queue_len_max) + return -ENOMEM; + + cb = can_rx_offload_get_cb(skb); + cb->timestamp = timestamp; + + spin_lock_irqsave(&offload->skb_queue.lock, flags); + __skb_queue_add_sort(&offload->skb_queue, skb, can_rx_offload_compare); + spin_unlock_irqrestore(&offload->skb_queue.lock, flags); + + can_rx_offload_schedule(offload); + + return 0; +} +EXPORT_SYMBOL_GPL(can_rx_offload_queue_sorted); + +unsigned int can_rx_offload_get_echo_skb(struct can_rx_offload *offload, + unsigned int idx, u32 timestamp) +{ + struct net_device *dev = offload->dev; + struct net_device_stats *stats = &dev->stats; + struct sk_buff *skb; + u8 len; + int err; + + skb = __can_get_echo_skb(dev, idx, &len); + if (!skb) + return 0; + + err = can_rx_offload_queue_sorted(offload, skb, timestamp); + if (err) { + stats->rx_errors++; + stats->tx_fifo_errors++; + } + + return len; +} +EXPORT_SYMBOL_GPL(can_rx_offload_get_echo_skb); + +int can_rx_offload_queue_tail(struct can_rx_offload *offload, + struct sk_buff *skb) { if (skb_queue_len(&offload->skb_queue) > offload->skb_queue_len_max) @@ -222,7 +269,7 @@ int can_rx_offload_irq_queue_err_skb(struct can_rx_offload *offload, struct sk_b return 0; } -EXPORT_SYMBOL_GPL(can_rx_offload_irq_queue_err_skb); +EXPORT_SYMBOL_GPL(can_rx_offload_queue_tail); static int can_rx_offload_init_queue(struct net_device *dev, struct can_rx_offload *offload, unsigned int weight) { diff --git a/drivers/net/can/spi/hi311x.c b/drivers/net/can/spi/hi311x.c index 53e320c92a8b..ddaf46239e39 100644 --- a/drivers/net/can/spi/hi311x.c +++ b/drivers/net/can/spi/hi311x.c @@ -760,7 +760,7 @@ static int hi3110_open(struct net_device *net) { struct hi3110_priv *priv = netdev_priv(net); struct spi_device *spi = priv->spi; - unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_RISING; + unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_HIGH; int ret; ret = open_candev(net); diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c index b939a4c10b84..c89c7d4900d7 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c @@ -528,7 +528,6 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, context = &priv->tx_contexts[i]; context->echo_index = i; - can_put_echo_skb(skb, netdev, context->echo_index); ++priv->active_tx_contexts; if (priv->active_tx_contexts >= (int)dev->max_tx_urbs) netif_stop_queue(netdev); @@ -553,7 +552,6 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, dev_kfree_skb(skb); spin_lock_irqsave(&priv->tx_contexts_lock, flags); - can_free_echo_skb(netdev, context->echo_index); context->echo_index = dev->max_tx_urbs; --priv->active_tx_contexts; netif_wake_queue(netdev); @@ -564,6 +562,8 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, context->priv = priv; + can_put_echo_skb(skb, netdev, context->echo_index); + usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out->bEndpointAddress), diff --git a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c index c084bae5ec0a..5fc0be564274 100644 --- a/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c +++ b/drivers/net/can/usb/kvaser_usb/kvaser_usb_hydra.c @@ -1019,6 +1019,11 @@ kvaser_usb_hydra_error_frame(struct kvaser_usb_net_priv *priv, new_state : CAN_STATE_ERROR_ACTIVE; can_change_state(netdev, cf, tx_state, rx_state); + + if (priv->can.restart_ms && + old_state >= CAN_STATE_BUS_OFF && + new_state < CAN_STATE_BUS_OFF) + cf->can_id |= CAN_ERR_RESTARTED; } if (new_state == CAN_STATE_BUS_OFF) { @@ -1028,11 +1033,6 @@ kvaser_usb_hydra_error_frame(struct kvaser_usb_net_priv *priv, can_bus_off(netdev); } - - if (priv->can.restart_ms && - old_state >= CAN_STATE_BUS_OFF && - new_state < CAN_STATE_BUS_OFF) - cf->can_id |= CAN_ERR_RESTARTED; } if (!skb) { diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c index 0678a38b1af4..f3d5bda012a1 100644 --- a/drivers/net/can/usb/ucan.c +++ b/drivers/net/can/usb/ucan.c @@ -35,10 +35,6 @@ #include <linux/slab.h> #include <linux/usb.h> -#include <linux/can.h> -#include <linux/can/dev.h> -#include <linux/can/error.h> - #define UCAN_DRIVER_NAME "ucan" #define UCAN_MAX_RX_URBS 8 /* the CAN controller needs a while to enable/disable the bus */ @@ -1575,11 +1571,8 @@ err_firmware_needs_update: /* disconnect the device */ static void ucan_disconnect(struct usb_interface *intf) { - struct usb_device *udev; struct ucan_priv *up = usb_get_intfdata(intf); - udev = interface_to_usbdev(intf); - usb_set_intfdata(intf, NULL); if (up) { diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c index 18956e7604a3..a70bb1bb90e7 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.c +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c @@ -1848,6 +1848,8 @@ static void ena_down(struct ena_adapter *adapter) rc = ena_com_dev_reset(adapter->ena_dev, adapter->reset_reason); if (rc) dev_err(&adapter->pdev->dev, "Device reset failed\n"); + /* stop submitting admin commands on a device that was reset */ + ena_com_set_admin_running_state(adapter->ena_dev, false); } ena_destroy_all_io_queues(adapter); @@ -1914,6 +1916,9 @@ static int ena_close(struct net_device *netdev) netif_dbg(adapter, ifdown, netdev, "%s\n", __func__); + if (!test_bit(ENA_FLAG_DEVICE_RUNNING, &adapter->flags)) + return 0; + if (test_bit(ENA_FLAG_DEV_UP, &adapter->flags)) ena_down(adapter); @@ -2613,9 +2618,7 @@ static void ena_destroy_device(struct ena_adapter *adapter, bool graceful) ena_down(adapter); /* Stop the device from sending AENQ events (in case reset flag is set - * and device is up, ena_close already reset the device - * In case the reset flag is set and the device is up, ena_down() - * already perform the reset, so it can be skipped. + * and device is up, ena_down() already reset the device. */ if (!(test_bit(ENA_FLAG_TRIGGER_RESET, &adapter->flags) && dev_up)) ena_com_dev_reset(adapter->ena_dev, adapter->reset_reason); @@ -2694,8 +2697,8 @@ err_device_destroy: ena_com_abort_admin_commands(ena_dev); ena_com_wait_for_abort_completion(ena_dev); ena_com_admin_destroy(ena_dev); - ena_com_mmio_reg_read_request_destroy(ena_dev); ena_com_dev_reset(ena_dev, ENA_REGS_RESET_DRIVER_INVALID_STATE); + ena_com_mmio_reg_read_request_destroy(ena_dev); err: clear_bit(ENA_FLAG_DEVICE_RUNNING, &adapter->flags); clear_bit(ENA_FLAG_ONGOING_RESET, &adapter->flags); @@ -3452,6 +3455,8 @@ err_rss: ena_com_rss_destroy(ena_dev); err_free_msix: ena_com_dev_reset(ena_dev, ENA_REGS_RESET_INIT_ERR); + /* stop submitting admin commands on a device that was reset */ + ena_com_set_admin_running_state(ena_dev, false); ena_free_mgmnt_irq(adapter); ena_disable_msix(adapter); err_worker_destroy: @@ -3498,18 +3503,12 @@ static void ena_remove(struct pci_dev *pdev) cancel_work_sync(&adapter->reset_task); - unregister_netdev(netdev); - - /* If the device is running then we want to make sure the device will be - * reset to make sure no more events will be issued by the device. - */ - if (test_bit(ENA_FLAG_DEVICE_RUNNING, &adapter->flags)) - set_bit(ENA_FLAG_TRIGGER_RESET, &adapter->flags); - rtnl_lock(); ena_destroy_device(adapter, true); rtnl_unlock(); + unregister_netdev(netdev); + free_netdev(netdev); ena_com_rss_destroy(ena_dev); diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.h b/drivers/net/ethernet/amazon/ena/ena_netdev.h index 521873642339..dc8b6173d8d8 100644 --- a/drivers/net/ethernet/amazon/ena/ena_netdev.h +++ b/drivers/net/ethernet/amazon/ena/ena_netdev.h @@ -45,7 +45,7 @@ #define DRV_MODULE_VER_MAJOR 2 #define DRV_MODULE_VER_MINOR 0 -#define DRV_MODULE_VER_SUBMINOR 1 +#define DRV_MODULE_VER_SUBMINOR 2 #define DRV_MODULE_NAME "ena" #ifndef DRV_MODULE_VERSION diff --git a/drivers/net/ethernet/amd/sunlance.c b/drivers/net/ethernet/amd/sunlance.c index b4fc0ed5bce8..9d4899826823 100644 --- a/drivers/net/ethernet/amd/sunlance.c +++ b/drivers/net/ethernet/amd/sunlance.c @@ -1419,7 +1419,7 @@ static int sparc_lance_probe_one(struct platform_device *op, prop = of_get_property(nd, "tpe-link-test?", NULL); if (!prop) - goto no_link_test; + goto node_put; if (strcmp(prop, "true")) { printk(KERN_NOTICE "SunLance: warning: overriding option " @@ -1428,6 +1428,8 @@ static int sparc_lance_probe_one(struct platform_device *op, "to ecd@skynet.be\n"); auxio_set_lte(AUXIO_LTE_ON); } +node_put: + of_node_put(nd); no_link_test: lp->auto_select = 1; lp->tpe = 0; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index be1506169076..0de487a8f0eb 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -2191,6 +2191,13 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id, #define PMF_DMAE_C(bp) (BP_PORT(bp) * MAX_DMAE_C_PER_PORT + \ E1HVN_MAX) +/* Following is the DMAE channel number allocation for the clients. + * MFW: OCBB/OCSD implementations use DMAE channels 14/15 respectively. + * Driver: 0-3 and 8-11 (for PF dmae operations) + * 4 and 12 (for stats requests) + */ +#define BNX2X_FW_DMAE_C 13 /* Channel for FW DMAE operations */ + /* PCIE link and speed */ #define PCICFG_LINK_WIDTH 0x1f00000 #define PCICFG_LINK_WIDTH_SHIFT 20 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c index 3f4d2c8da21a..a9eaaf3e73a4 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c @@ -6149,6 +6149,7 @@ static inline int bnx2x_func_send_start(struct bnx2x *bp, rdata->sd_vlan_tag = cpu_to_le16(start_params->sd_vlan_tag); rdata->path_id = BP_PATH(bp); rdata->network_cos_mode = start_params->network_cos_mode; + rdata->dmae_cmd_id = BNX2X_FW_DMAE_C; rdata->vxlan_dst_port = cpu_to_le16(start_params->vxlan_dst_port); rdata->geneve_dst_port = cpu_to_le16(start_params->geneve_dst_port); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index dd85d790f638..d4c300117529 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -1675,7 +1675,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr, } else { if (rxcmp1->rx_cmp_cfa_code_errors_v2 & RX_CMP_L4_CS_ERR_BITS) { if (dev->features & NETIF_F_RXCSUM) - cpr->rx_l4_csum_errors++; + bnapi->cp_ring.rx_l4_csum_errors++; } } @@ -8714,6 +8714,26 @@ static int bnxt_set_features(struct net_device *dev, netdev_features_t features) return rc; } +static int bnxt_dbg_hwrm_ring_info_get(struct bnxt *bp, u8 ring_type, + u32 ring_id, u32 *prod, u32 *cons) +{ + struct hwrm_dbg_ring_info_get_output *resp = bp->hwrm_cmd_resp_addr; + struct hwrm_dbg_ring_info_get_input req = {0}; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_DBG_RING_INFO_GET, -1, -1); + req.ring_type = ring_type; + req.fw_ring_id = cpu_to_le32(ring_id); + mutex_lock(&bp->hwrm_cmd_lock); + rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (!rc) { + *prod = le32_to_cpu(resp->producer_index); + *cons = le32_to_cpu(resp->consumer_index); + } + mutex_unlock(&bp->hwrm_cmd_lock); + return rc; +} + static void bnxt_dump_tx_sw_state(struct bnxt_napi *bnapi) { struct bnxt_tx_ring_info *txr = bnapi->tx_ring; @@ -8821,6 +8841,11 @@ static void bnxt_timer(struct timer_list *t) bnxt_queue_sp_work(bp); } } + + if ((bp->flags & BNXT_FLAG_CHIP_P5) && netif_carrier_ok(dev)) { + set_bit(BNXT_RING_COAL_NOW_SP_EVENT, &bp->sp_event); + bnxt_queue_sp_work(bp); + } bnxt_restart_timer: mod_timer(&bp->timer, jiffies + bp->current_interval); } @@ -8851,6 +8876,44 @@ static void bnxt_reset(struct bnxt *bp, bool silent) bnxt_rtnl_unlock_sp(bp); } +static void bnxt_chk_missed_irq(struct bnxt *bp) +{ + int i; + + if (!(bp->flags & BNXT_FLAG_CHIP_P5)) + return; + + for (i = 0; i < bp->cp_nr_rings; i++) { + struct bnxt_napi *bnapi = bp->bnapi[i]; + struct bnxt_cp_ring_info *cpr; + u32 fw_ring_id; + int j; + + if (!bnapi) + continue; + + cpr = &bnapi->cp_ring; + for (j = 0; j < 2; j++) { + struct bnxt_cp_ring_info *cpr2 = cpr->cp_ring_arr[j]; + u32 val[2]; + + if (!cpr2 || cpr2->has_more_work || + !bnxt_has_work(bp, cpr2)) + continue; + + if (cpr2->cp_raw_cons != cpr2->last_cp_raw_cons) { + cpr2->last_cp_raw_cons = cpr2->cp_raw_cons; + continue; + } + fw_ring_id = cpr2->cp_ring_struct.fw_ring_id; + bnxt_dbg_hwrm_ring_info_get(bp, + DBG_RING_INFO_GET_REQ_RING_TYPE_L2_CMPL, + fw_ring_id, &val[0], &val[1]); + cpr->missed_irqs++; + } + } +} + static void bnxt_cfg_ntp_filters(struct bnxt *); static void bnxt_sp_task(struct work_struct *work) @@ -8930,6 +8993,9 @@ static void bnxt_sp_task(struct work_struct *work) if (test_and_clear_bit(BNXT_FLOW_STATS_SP_EVENT, &bp->sp_event)) bnxt_tc_flow_stats_work(bp); + if (test_and_clear_bit(BNXT_RING_COAL_NOW_SP_EVENT, &bp->sp_event)) + bnxt_chk_missed_irq(bp); + /* These functions below will clear BNXT_STATE_IN_SP_TASK. They * must be the last functions to be called before exiting. */ @@ -10087,6 +10153,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } bnxt_hwrm_func_qcfg(bp); + bnxt_hwrm_vnic_qcaps(bp); bnxt_hwrm_port_led_qcaps(bp); bnxt_ethtool_init(bp); bnxt_dcb_init(bp); @@ -10120,7 +10187,6 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) VNIC_RSS_CFG_REQ_HASH_TYPE_UDP_IPV6; } - bnxt_hwrm_vnic_qcaps(bp); if (bnxt_rfs_supported(bp)) { dev->hw_features |= NETIF_F_NTUPLE; if (bnxt_rfs_capable(bp)) { diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 498b373c992d..9e99d4ab3e06 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -798,6 +798,8 @@ struct bnxt_cp_ring_info { u8 had_work_done:1; u8 has_more_work:1; + u32 last_cp_raw_cons; + struct bnxt_coal rx_ring_coal; u64 rx_packets; u64 rx_bytes; @@ -816,6 +818,7 @@ struct bnxt_cp_ring_info { dma_addr_t hw_stats_map; u32 hw_stats_ctx_id; u64 rx_l4_csum_errors; + u64 missed_irqs; struct bnxt_ring_struct cp_ring_struct; @@ -1527,6 +1530,7 @@ struct bnxt { #define BNXT_LINK_SPEED_CHNG_SP_EVENT 14 #define BNXT_FLOW_STATS_SP_EVENT 15 #define BNXT_UPDATE_PHY_SP_EVENT 16 +#define BNXT_RING_COAL_NOW_SP_EVENT 17 struct bnxt_hw_resc hw_resc; struct bnxt_pf_info pf; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 48078564f025..6cc69a58478a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -137,7 +137,7 @@ reset_coalesce: return rc; } -#define BNXT_NUM_STATS 21 +#define BNXT_NUM_STATS 22 #define BNXT_RX_STATS_ENTRY(counter) \ { BNXT_RX_STATS_OFFSET(counter), __stringify(counter) } @@ -384,6 +384,7 @@ static void bnxt_get_ethtool_stats(struct net_device *dev, for (k = 0; k < stat_fields; j++, k++) buf[j] = le64_to_cpu(hw_stats[k]); buf[j++] = cpr->rx_l4_csum_errors; + buf[j++] = cpr->missed_irqs; bnxt_sw_func_stats[RX_TOTAL_DISCARDS].counter += le64_to_cpu(cpr->hw_stats->rx_discard_pkts); @@ -468,6 +469,8 @@ static void bnxt_get_strings(struct net_device *dev, u32 stringset, u8 *buf) buf += ETH_GSTRING_LEN; sprintf(buf, "[%d]: rx_l4_csum_errors", i); buf += ETH_GSTRING_LEN; + sprintf(buf, "[%d]: missed_irqs", i); + buf += ETH_GSTRING_LEN; } for (i = 0; i < BNXT_NUM_SW_FUNC_STATS; i++) { strcpy(buf, bnxt_sw_func_stats[i].string); @@ -2942,8 +2945,8 @@ bnxt_fill_coredump_record(struct bnxt *bp, struct bnxt_coredump_record *record, record->asic_state = 0; strlcpy(record->system_name, utsname()->nodename, sizeof(record->system_name)); - record->year = cpu_to_le16(tm.tm_year); - record->month = cpu_to_le16(tm.tm_mon); + record->year = cpu_to_le16(tm.tm_year + 1900); + record->month = cpu_to_le16(tm.tm_mon + 1); record->day = cpu_to_le16(tm.tm_mday); record->hour = cpu_to_le16(tm.tm_hour); record->minute = cpu_to_le16(tm.tm_min); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c index beee61292d5e..b59b382d34f9 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c @@ -43,6 +43,9 @@ static int bnxt_register_dev(struct bnxt_en_dev *edev, int ulp_id, if (ulp_id == BNXT_ROCE_ULP) { unsigned int max_stat_ctxs; + if (bp->flags & BNXT_FLAG_CHIP_P5) + return -EOPNOTSUPP; + max_stat_ctxs = bnxt_get_max_func_stat_ctxs(bp); if (max_stat_ctxs <= BNXT_MIN_ROCE_STAT_CTXS || bp->num_stat_ctxs == max_stat_ctxs) diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 89295306f161..432c3b867084 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -12422,6 +12422,7 @@ static int tg3_set_ringparam(struct net_device *dev, struct ethtool_ringparam *e { struct tg3 *tp = netdev_priv(dev); int i, irq_sync = 0, err = 0; + bool reset_phy = false; if ((ering->rx_pending > tp->rx_std_ring_mask) || (ering->rx_jumbo_pending > tp->rx_jmb_ring_mask) || @@ -12453,7 +12454,13 @@ static int tg3_set_ringparam(struct net_device *dev, struct ethtool_ringparam *e if (netif_running(dev)) { tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); - err = tg3_restart_hw(tp, false); + /* Reset PHY to avoid PHY lock up */ + if (tg3_asic_rev(tp) == ASIC_REV_5717 || + tg3_asic_rev(tp) == ASIC_REV_5719 || + tg3_asic_rev(tp) == ASIC_REV_5720) + reset_phy = true; + + err = tg3_restart_hw(tp, reset_phy); if (!err) tg3_netif_start(tp); } @@ -12487,6 +12494,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam { struct tg3 *tp = netdev_priv(dev); int err = 0; + bool reset_phy = false; if (tp->link_config.autoneg == AUTONEG_ENABLE) tg3_warn_mgmt_link_flap(tp); @@ -12556,7 +12564,13 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam if (netif_running(dev)) { tg3_halt(tp, RESET_KIND_SHUTDOWN, 1); - err = tg3_restart_hw(tp, false); + /* Reset PHY to avoid PHY lock up */ + if (tg3_asic_rev(tp) == ASIC_REV_5717 || + tg3_asic_rev(tp) == ASIC_REV_5719 || + tg3_asic_rev(tp) == ASIC_REV_5720) + reset_phy = true; + + err = tg3_restart_hw(tp, reset_phy); if (!err) tg3_netif_start(tp); } diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 768f584f8392..88f8a8fa93cd 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -1784,6 +1784,7 @@ static int nicvf_xdp_setup(struct nicvf *nic, struct bpf_prog *prog) bool if_up = netif_running(nic->netdev); struct bpf_prog *old_prog; bool bpf_attached = false; + int ret = 0; /* For now just support only the usual MTU sized frames */ if (prog && (dev->mtu > 1500)) { @@ -1817,8 +1818,12 @@ static int nicvf_xdp_setup(struct nicvf *nic, struct bpf_prog *prog) if (nic->xdp_prog) { /* Attach BPF program */ nic->xdp_prog = bpf_prog_add(nic->xdp_prog, nic->rx_queues - 1); - if (!IS_ERR(nic->xdp_prog)) + if (!IS_ERR(nic->xdp_prog)) { bpf_attached = true; + } else { + ret = PTR_ERR(nic->xdp_prog); + nic->xdp_prog = NULL; + } } /* Calculate Tx queues needed for XDP and network stack */ @@ -1830,7 +1835,7 @@ static int nicvf_xdp_setup(struct nicvf *nic, struct bpf_prog *prog) netif_trans_update(nic->netdev); } - return 0; + return ret; } static int nicvf_xdp(struct net_device *netdev, struct netdev_bpf *xdp) diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c index 187a249ff2d1..fcaf18fa3904 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.c @@ -585,10 +585,12 @@ static void nicvf_free_snd_queue(struct nicvf *nic, struct snd_queue *sq) if (!sq->dmem.base) return; - if (sq->tso_hdrs) + if (sq->tso_hdrs) { dma_free_coherent(&nic->pdev->dev, sq->dmem.q_len * TSO_HEADER_SIZE, sq->tso_hdrs, sq->tso_hdrs_phys); + sq->tso_hdrs = NULL; + } /* Free pending skbs in the queue */ smp_rmb(); diff --git a/drivers/net/ethernet/chelsio/Kconfig b/drivers/net/ethernet/chelsio/Kconfig index 75c1c5ed2387..e2cdfa75673f 100644 --- a/drivers/net/ethernet/chelsio/Kconfig +++ b/drivers/net/ethernet/chelsio/Kconfig @@ -67,7 +67,6 @@ config CHELSIO_T3 config CHELSIO_T4 tristate "Chelsio Communications T4/T5/T6 Ethernet support" depends on PCI && (IPV6 || IPV6=n) - depends on THERMAL || !THERMAL select FW_LOADER select MDIO select ZLIB_DEFLATE diff --git a/drivers/net/ethernet/chelsio/cxgb4/Makefile b/drivers/net/ethernet/chelsio/cxgb4/Makefile index 78e5d17a1d5f..91d8a885deba 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/Makefile +++ b/drivers/net/ethernet/chelsio/cxgb4/Makefile @@ -12,6 +12,4 @@ cxgb4-objs := cxgb4_main.o l2t.o smt.o t4_hw.o sge.o clip_tbl.o cxgb4_ethtool.o cxgb4-$(CONFIG_CHELSIO_T4_DCB) += cxgb4_dcb.o cxgb4-$(CONFIG_CHELSIO_T4_FCOE) += cxgb4_fcoe.o cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o -ifdef CONFIG_THERMAL -cxgb4-objs += cxgb4_thermal.o -endif +cxgb4-$(CONFIG_THERMAL) += cxgb4_thermal.o diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 05a46926016a..d49db46254cd 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -5863,7 +5863,7 @@ fw_attach_fail: if (!is_t4(adapter->params.chip)) cxgb4_ptp_init(adapter); - if (IS_ENABLED(CONFIG_THERMAL) && + if (IS_REACHABLE(CONFIG_THERMAL) && !is_t4(adapter->params.chip) && (adapter->flags & FW_OK)) cxgb4_thermal_init(adapter); @@ -5932,7 +5932,7 @@ static void remove_one(struct pci_dev *pdev) if (!is_t4(adapter->params.chip)) cxgb4_ptp_stop(adapter); - if (IS_ENABLED(CONFIG_THERMAL)) + if (IS_REACHABLE(CONFIG_THERMAL)) cxgb4_thermal_remove(adapter); /* If we allocated filters, free up state associated with any diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index ceec467f590d..949103db8a8a 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -660,7 +660,7 @@ static void gmac_clean_txq(struct net_device *netdev, struct gmac_txq *txq, u64_stats_update_begin(&port->tx_stats_syncp); port->tx_frag_stats[nfrags]++; - u64_stats_update_end(&port->ir_stats_syncp); + u64_stats_update_end(&port->tx_stats_syncp); } } diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index 570caeb8ee9e..084f24daf2b5 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -872,11 +872,10 @@ static irqreturn_t ftmac100_interrupt(int irq, void *dev_id) struct net_device *netdev = dev_id; struct ftmac100 *priv = netdev_priv(netdev); - if (likely(netif_running(netdev))) { - /* Disable interrupts for polling */ - ftmac100_disable_all_int(priv); + /* Disable interrupts for polling */ + ftmac100_disable_all_int(priv); + if (likely(netif_running(netdev))) napi_schedule(&priv->napi); - } return IRQ_HANDLED; } diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index c9d5d0a7fbf1..c0203a0d5e3b 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -485,8 +485,8 @@ static void release_rx_pools(struct ibmvnic_adapter *adapter) for (j = 0; j < rx_pool->size; j++) { if (rx_pool->rx_buff[j].skb) { - dev_kfree_skb_any(rx_pool->rx_buff[i].skb); - rx_pool->rx_buff[i].skb = NULL; + dev_kfree_skb_any(rx_pool->rx_buff[j].skb); + rx_pool->rx_buff[j].skb = NULL; } } @@ -1103,20 +1103,15 @@ static int ibmvnic_open(struct net_device *netdev) return 0; } - mutex_lock(&adapter->reset_lock); - if (adapter->state != VNIC_CLOSED) { rc = ibmvnic_login(netdev); - if (rc) { - mutex_unlock(&adapter->reset_lock); + if (rc) return rc; - } rc = init_resources(adapter); if (rc) { netdev_err(netdev, "failed to initialize resources\n"); release_resources(adapter); - mutex_unlock(&adapter->reset_lock); return rc; } } @@ -1124,8 +1119,6 @@ static int ibmvnic_open(struct net_device *netdev) rc = __ibmvnic_open(netdev); netif_carrier_on(netdev); - mutex_unlock(&adapter->reset_lock); - return rc; } @@ -1269,10 +1262,8 @@ static int ibmvnic_close(struct net_device *netdev) return 0; } - mutex_lock(&adapter->reset_lock); rc = __ibmvnic_close(netdev); ibmvnic_cleanup(netdev); - mutex_unlock(&adapter->reset_lock); return rc; } @@ -1746,6 +1737,7 @@ static int do_reset(struct ibmvnic_adapter *adapter, struct ibmvnic_rwi *rwi, u32 reset_state) { u64 old_num_rx_queues, old_num_tx_queues; + u64 old_num_rx_slots, old_num_tx_slots; struct net_device *netdev = adapter->netdev; int i, rc; @@ -1757,6 +1749,8 @@ static int do_reset(struct ibmvnic_adapter *adapter, old_num_rx_queues = adapter->req_rx_queues; old_num_tx_queues = adapter->req_tx_queues; + old_num_rx_slots = adapter->req_rx_add_entries_per_subcrq; + old_num_tx_slots = adapter->req_tx_entries_per_subcrq; ibmvnic_cleanup(netdev); @@ -1819,21 +1813,20 @@ static int do_reset(struct ibmvnic_adapter *adapter, if (rc) return rc; } else if (adapter->req_rx_queues != old_num_rx_queues || - adapter->req_tx_queues != old_num_tx_queues) { - adapter->map_id = 1; + adapter->req_tx_queues != old_num_tx_queues || + adapter->req_rx_add_entries_per_subcrq != + old_num_rx_slots || + adapter->req_tx_entries_per_subcrq != + old_num_tx_slots) { release_rx_pools(adapter); release_tx_pools(adapter); - rc = init_rx_pools(netdev); - if (rc) - return rc; - rc = init_tx_pools(netdev); - if (rc) - return rc; - release_napi(adapter); - rc = init_napi(adapter); + release_vpd_data(adapter); + + rc = init_resources(adapter); if (rc) return rc; + } else { rc = reset_tx_pools(adapter); if (rc) @@ -1917,17 +1910,8 @@ static int do_hard_reset(struct ibmvnic_adapter *adapter, adapter->state = VNIC_PROBED; return 0; } - /* netif_set_real_num_xx_queues needs to take rtnl lock here - * unless wait_for_reset is set, in which case the rtnl lock - * has already been taken before initializing the reset - */ - if (!adapter->wait_for_reset) { - rtnl_lock(); - rc = init_resources(adapter); - rtnl_unlock(); - } else { - rc = init_resources(adapter); - } + + rc = init_resources(adapter); if (rc) return rc; @@ -1986,13 +1970,21 @@ static void __ibmvnic_reset(struct work_struct *work) struct ibmvnic_rwi *rwi; struct ibmvnic_adapter *adapter; struct net_device *netdev; + bool we_lock_rtnl = false; u32 reset_state; int rc = 0; adapter = container_of(work, struct ibmvnic_adapter, ibmvnic_reset); netdev = adapter->netdev; - mutex_lock(&adapter->reset_lock); + /* netif_set_real_num_xx_queues needs to take rtnl lock here + * unless wait_for_reset is set, in which case the rtnl lock + * has already been taken before initializing the reset + */ + if (!adapter->wait_for_reset) { + rtnl_lock(); + we_lock_rtnl = true; + } reset_state = adapter->state; rwi = get_next_rwi(adapter); @@ -2020,12 +2012,11 @@ static void __ibmvnic_reset(struct work_struct *work) if (rc) { netdev_dbg(adapter->netdev, "Reset failed\n"); free_all_rwi(adapter); - mutex_unlock(&adapter->reset_lock); - return; } adapter->resetting = false; - mutex_unlock(&adapter->reset_lock); + if (we_lock_rtnl) + rtnl_unlock(); } static int ibmvnic_reset(struct ibmvnic_adapter *adapter, @@ -4768,7 +4759,6 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) INIT_WORK(&adapter->ibmvnic_reset, __ibmvnic_reset); INIT_LIST_HEAD(&adapter->rwi_list); - mutex_init(&adapter->reset_lock); mutex_init(&adapter->rwi_lock); adapter->resetting = false; @@ -4840,8 +4830,8 @@ static int ibmvnic_remove(struct vio_dev *dev) struct ibmvnic_adapter *adapter = netdev_priv(netdev); adapter->state = VNIC_REMOVING; - unregister_netdev(netdev); - mutex_lock(&adapter->reset_lock); + rtnl_lock(); + unregister_netdevice(netdev); release_resources(adapter); release_sub_crqs(adapter, 1); @@ -4852,7 +4842,7 @@ static int ibmvnic_remove(struct vio_dev *dev) adapter->state = VNIC_REMOVED; - mutex_unlock(&adapter->reset_lock); + rtnl_unlock(); device_remove_file(&dev->dev, &dev_attr_failover); free_netdev(netdev); dev_set_drvdata(&dev->dev, NULL); diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 18103b811d4d..99c4f8d331ce 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -1075,7 +1075,7 @@ struct ibmvnic_adapter { struct tasklet_struct tasklet; enum vnic_state state; enum ibmvnic_reset_reason reset_reason; - struct mutex reset_lock, rwi_lock; + struct mutex rwi_lock; struct list_head rwi_list; struct work_struct ibmvnic_reset; bool resetting; diff --git a/drivers/net/ethernet/lantiq_xrx200.c b/drivers/net/ethernet/lantiq_xrx200.c index 8c5ba4b81fb7..2d4d10a017e5 100644 --- a/drivers/net/ethernet/lantiq_xrx200.c +++ b/drivers/net/ethernet/lantiq_xrx200.c @@ -512,7 +512,8 @@ static int xrx200_probe(struct platform_device *pdev) err = register_netdev(net_dev); if (err) goto err_unprepare_clk; - return err; + + return 0; err_unprepare_clk: clk_disable_unprepare(priv->clk); @@ -520,7 +521,7 @@ err_unprepare_clk: err_uninit_dma: xrx200_hw_cleanup(priv); - return 0; + return err; } static int xrx200_remove(struct platform_device *pdev) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 3ba672e9e353..e5397c8197b9 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3343,7 +3343,6 @@ static void mvneta_validate(struct net_device *ndev, unsigned long *supported, if (state->interface != PHY_INTERFACE_MODE_NA && state->interface != PHY_INTERFACE_MODE_QSGMII && state->interface != PHY_INTERFACE_MODE_SGMII && - state->interface != PHY_INTERFACE_MODE_2500BASEX && !phy_interface_mode_is_8023z(state->interface) && !phy_interface_mode_is_rgmii(state->interface)) { bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); @@ -3357,14 +3356,9 @@ static void mvneta_validate(struct net_device *ndev, unsigned long *supported, /* Asymmetric pause is unsupported */ phylink_set(mask, Pause); - /* We cannot use 1Gbps when using the 2.5G interface. */ - if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { - phylink_set(mask, 2500baseT_Full); - phylink_set(mask, 2500baseX_Full); - } else { - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); - } + /* Half-duplex at speeds higher than 100Mbit is unsupported */ + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); if (!phy_interface_mode_is_8023z(state->interface)) { /* 10M and 100M are only supported in non-802.3z mode */ diff --git a/drivers/net/ethernet/mellanox/mlx4/alloc.c b/drivers/net/ethernet/mellanox/mlx4/alloc.c index deef5a998985..9af34e03892c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx4/alloc.c @@ -337,7 +337,7 @@ void mlx4_zone_allocator_destroy(struct mlx4_zone_allocator *zone_alloc) static u32 __mlx4_alloc_from_zone(struct mlx4_zone_entry *zone, int count, int align, u32 skip_mask, u32 *puid) { - u32 uid; + u32 uid = 0; u32 res; struct mlx4_zone_allocator *zone_alloc = zone->allocator; struct mlx4_zone_entry *curr_node; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index ebcd2778eeb3..23f1b5b512c2 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -540,8 +540,8 @@ struct slave_list { struct resource_allocator { spinlock_t alloc_lock; /* protect quotas */ union { - int res_reserved; - int res_port_rsvd[MLX4_MAX_PORTS]; + unsigned int res_reserved; + unsigned int res_port_rsvd[MLX4_MAX_PORTS]; }; union { int res_free; diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c index 2e84f10f59ba..1a11bc0e1612 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mr.c +++ b/drivers/net/ethernet/mellanox/mlx4/mr.c @@ -363,6 +363,7 @@ int mlx4_mr_hw_write_mpt(struct mlx4_dev *dev, struct mlx4_mr *mmr, container_of((void *)mpt_entry, struct mlx4_cmd_mailbox, buf); + (*mpt_entry)->lkey = 0; err = mlx4_SW2HW_MPT(dev, mailbox, key); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index d7fbd5b6ac95..118324802926 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -569,6 +569,7 @@ struct mlx5e_rq { unsigned long state; int ix; + unsigned int hw_mtu; struct net_dim dim; /* Dynamic Interrupt Moderation */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/port.c b/drivers/net/ethernet/mellanox/mlx5/core/en/port.c index 023dc4bccd28..4a37713023be 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/port.c @@ -88,10 +88,8 @@ int mlx5e_port_linkspeed(struct mlx5_core_dev *mdev, u32 *speed) eth_proto_oper = MLX5_GET(ptys_reg, out, eth_proto_oper); *speed = mlx5e_port_ptys2speed(eth_proto_oper); - if (!(*speed)) { - mlx5_core_warn(mdev, "cannot get port speed\n"); + if (!(*speed)) err = -EINVAL; - } return err; } @@ -258,7 +256,7 @@ static int mlx5e_fec_admin_field(u32 *pplm, case 40000: if (!write) *fec_policy = MLX5_GET(pplm_reg, pplm, - fec_override_cap_10g_40g); + fec_override_admin_10g_40g); else MLX5_SET(pplm_reg, pplm, fec_override_admin_10g_40g, *fec_policy); @@ -310,7 +308,7 @@ static int mlx5e_get_fec_cap_field(u32 *pplm, case 10000: case 40000: *fec_cap = MLX5_GET(pplm_reg, pplm, - fec_override_admin_10g_40g); + fec_override_cap_10g_40g); break; case 25000: *fec_cap = MLX5_GET(pplm_reg, pplm, @@ -394,12 +392,12 @@ int mlx5e_get_fec_mode(struct mlx5_core_dev *dev, u32 *fec_mode_active, int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u8 fec_policy) { + u8 fec_policy_nofec = BIT(MLX5E_FEC_NOFEC); bool fec_mode_not_supp_in_speed = false; - u8 no_fec_policy = BIT(MLX5E_FEC_NOFEC); u32 out[MLX5_ST_SZ_DW(pplm_reg)] = {}; u32 in[MLX5_ST_SZ_DW(pplm_reg)] = {}; int sz = MLX5_ST_SZ_BYTES(pplm_reg); - u32 current_fec_speed; + u8 fec_policy_auto = 0; u8 fec_caps = 0; int err; int i; @@ -415,23 +413,19 @@ int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u8 fec_policy) if (err) return err; - err = mlx5e_port_linkspeed(dev, ¤t_fec_speed); - if (err) - return err; + MLX5_SET(pplm_reg, out, local_port, 1); - memset(in, 0, sz); - MLX5_SET(pplm_reg, in, local_port, 1); - for (i = 0; i < MLX5E_FEC_SUPPORTED_SPEEDS && !!fec_policy; i++) { + for (i = 0; i < MLX5E_FEC_SUPPORTED_SPEEDS; i++) { mlx5e_get_fec_cap_field(out, &fec_caps, fec_supported_speeds[i]); - /* policy supported for link speed */ - if (!!(fec_caps & fec_policy)) { - mlx5e_fec_admin_field(in, &fec_policy, 1, + /* policy supported for link speed, or policy is auto */ + if (fec_caps & fec_policy || fec_policy == fec_policy_auto) { + mlx5e_fec_admin_field(out, &fec_policy, 1, fec_supported_speeds[i]); } else { - if (fec_supported_speeds[i] == current_fec_speed) - return -EOPNOTSUPP; - mlx5e_fec_admin_field(in, &no_fec_policy, 1, - fec_supported_speeds[i]); + /* turn off FEC if supported. Else, leave it the same */ + if (fec_caps & fec_policy_nofec) + mlx5e_fec_admin_field(out, &fec_policy_nofec, 1, + fec_supported_speeds[i]); fec_mode_not_supp_in_speed = true; } } @@ -441,5 +435,5 @@ int mlx5e_set_fec_mode(struct mlx5_core_dev *dev, u8 fec_policy) "FEC policy 0x%x is not supported for some speeds", fec_policy); - return mlx5_core_access_reg(dev, in, sz, out, sz, MLX5_REG_PPLM, 0, 1); + return mlx5_core_access_reg(dev, out, sz, out, sz, MLX5_REG_PPLM, 0, 1); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/port_buffer.c b/drivers/net/ethernet/mellanox/mlx5/core/en/port_buffer.c index c047da8752da..eac245a93f91 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/port_buffer.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/port_buffer.c @@ -130,8 +130,10 @@ static u32 calculate_xoff(struct mlx5e_priv *priv, unsigned int mtu) int err; err = mlx5e_port_linkspeed(priv->mdev, &speed); - if (err) + if (err) { + mlx5_core_warn(priv->mdev, "cannot get port speed\n"); return 0; + } xoff = (301 + 216 * priv->dcbx.cable_len / 100) * speed / 1000 + 272 * mtu / 100; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index 3e770abfd802..25c1c4f96841 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -843,8 +843,7 @@ static int mlx5e_get_link_ksettings(struct net_device *netdev, ethtool_link_ksettings_add_link_mode(link_ksettings, supported, Autoneg); - err = get_fec_supported_advertised(mdev, link_ksettings); - if (err) + if (get_fec_supported_advertised(mdev, link_ksettings)) netdev_dbg(netdev, "%s: FEC caps query failed: %d\n", __func__, err); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 1243edbedc9e..871313d6b34d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -502,6 +502,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, rq->channel = c; rq->ix = c->ix; rq->mdev = mdev; + rq->hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu); rq->stats = &c->priv->channel_stats[c->ix].rq; rq->xdp_prog = params->xdp_prog ? bpf_prog_inc(params->xdp_prog) : NULL; @@ -1623,13 +1624,15 @@ static int mlx5e_alloc_cq_common(struct mlx5_core_dev *mdev, int err; u32 i; + err = mlx5_vector2eqn(mdev, param->eq_ix, &eqn_not_used, &irqn); + if (err) + return err; + err = mlx5_cqwq_create(mdev, ¶m->wq, param->cqc, &cq->wq, &cq->wq_ctrl); if (err) return err; - mlx5_vector2eqn(mdev, param->eq_ix, &eqn_not_used, &irqn); - mcq->cqe_sz = 64; mcq->set_ci_db = cq->wq_ctrl.db.db; mcq->arm_db = cq->wq_ctrl.db.db + 1; @@ -1687,6 +1690,10 @@ static int mlx5e_create_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param) int eqn; int err; + err = mlx5_vector2eqn(mdev, param->eq_ix, &eqn, &irqn_not_used); + if (err) + return err; + inlen = MLX5_ST_SZ_BYTES(create_cq_in) + sizeof(u64) * cq->wq_ctrl.buf.npages; in = kvzalloc(inlen, GFP_KERNEL); @@ -1700,8 +1707,6 @@ static int mlx5e_create_cq(struct mlx5e_cq *cq, struct mlx5e_cq_param *param) mlx5_fill_page_frag_array(&cq->wq_ctrl.buf, (__be64 *)MLX5_ADDR_OF(create_cq_in, in, pas)); - mlx5_vector2eqn(mdev, param->eq_ix, &eqn, &irqn_not_used); - MLX5_SET(cqc, cqc, cq_period_mode, param->cq_period_mode); MLX5_SET(cqc, cqc, c_eqn, eqn); MLX5_SET(cqc, cqc, uar_page, mdev->priv.uar->index); @@ -1921,6 +1926,10 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, int err; int eqn; + err = mlx5_vector2eqn(priv->mdev, ix, &eqn, &irq); + if (err) + return err; + c = kvzalloc_node(sizeof(*c), GFP_KERNEL, cpu_to_node(cpu)); if (!c) return -ENOMEM; @@ -1937,7 +1946,6 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, c->xdp = !!params->xdp_prog; c->stats = &priv->channel_stats[ix].ch; - mlx5_vector2eqn(priv->mdev, ix, &eqn, &irq); c->irq_desc = irq_to_desc(irq); netif_napi_add(netdev, &c->napi, mlx5e_napi_poll, 64); @@ -3574,6 +3582,7 @@ static int set_feature_cvlan_filter(struct net_device *netdev, bool enable) return 0; } +#ifdef CONFIG_MLX5_ESWITCH static int set_feature_tc_num_filters(struct net_device *netdev, bool enable) { struct mlx5e_priv *priv = netdev_priv(netdev); @@ -3586,6 +3595,7 @@ static int set_feature_tc_num_filters(struct net_device *netdev, bool enable) return 0; } +#endif static int set_feature_rx_all(struct net_device *netdev, bool enable) { @@ -3684,7 +3694,9 @@ static int mlx5e_set_features(struct net_device *netdev, err |= MLX5E_HANDLE_FEATURE(NETIF_F_LRO, set_feature_lro); err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_VLAN_CTAG_FILTER, set_feature_cvlan_filter); +#ifdef CONFIG_MLX5_ESWITCH err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_TC, set_feature_tc_num_filters); +#endif err |= MLX5E_HANDLE_FEATURE(NETIF_F_RXALL, set_feature_rx_all); err |= MLX5E_HANDLE_FEATURE(NETIF_F_RXFCS, set_feature_rx_fcs); err |= MLX5E_HANDLE_FEATURE(NETIF_F_HW_VLAN_CTAG_RX, set_feature_rx_vlan); @@ -3755,10 +3767,11 @@ int mlx5e_change_mtu(struct net_device *netdev, int new_mtu, } if (params->rq_wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) { + bool is_linear = mlx5e_rx_mpwqe_is_linear_skb(priv->mdev, &new_channels.params); u8 ppw_old = mlx5e_mpwqe_log_pkts_per_wqe(params); u8 ppw_new = mlx5e_mpwqe_log_pkts_per_wqe(&new_channels.params); - reset = reset && (ppw_old != ppw_new); + reset = reset && (is_linear || (ppw_old != ppw_new)); } if (!reset) { @@ -4678,7 +4691,9 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) FT_CAP(modify_root) && FT_CAP(identified_miss_table_mode) && FT_CAP(flow_table_modify)) { +#ifdef CONFIG_MLX5_ESWITCH netdev->hw_features |= NETIF_F_HW_TC; +#endif #ifdef CONFIG_MLX5_EN_ARFS netdev->hw_features |= NETIF_F_NTUPLE; #endif @@ -5004,11 +5019,21 @@ err_free_netdev: int mlx5e_attach_netdev(struct mlx5e_priv *priv) { const struct mlx5e_profile *profile; + int max_nch; int err; profile = priv->profile; clear_bit(MLX5E_STATE_DESTROYING, &priv->state); + /* max number of channels may have changed */ + max_nch = mlx5e_get_max_num_channels(priv->mdev); + if (priv->channels.params.num_channels > max_nch) { + mlx5_core_warn(priv->mdev, "MLX5E: Reducing number of channels to %d\n", max_nch); + priv->channels.params.num_channels = max_nch; + mlx5e_build_default_indir_rqt(priv->channels.params.indirection_rqt, + MLX5E_INDIR_RQT_SIZE, max_nch); + } + err = profile->init_tx(priv); if (err) goto out; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 79638dcbae78..16985ca3248d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -1104,6 +1104,12 @@ mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, u32 frag_size; bool consumed; + /* Check packet size. Note LRO doesn't use linear SKB */ + if (unlikely(cqe_bcnt > rq->hw_mtu)) { + rq->stats->oversize_pkts_sw_drop++; + return NULL; + } + va = page_address(di->page) + head_offset; data = va + rx_headroom; frag_size = MLX5_SKB_FRAG_SZ(rx_headroom + cqe_bcnt32); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c index 35ded91203f5..4382ef85488c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_selftest.c @@ -98,18 +98,17 @@ static int mlx5e_test_link_speed(struct mlx5e_priv *priv) return 1; } -#ifdef CONFIG_INET -/* loopback test */ -#define MLX5E_TEST_PKT_SIZE (MLX5E_RX_MAX_HEAD - NET_IP_ALIGN) -static const char mlx5e_test_text[ETH_GSTRING_LEN] = "MLX5E SELF TEST"; -#define MLX5E_TEST_MAGIC 0x5AEED15C001ULL - struct mlx5ehdr { __be32 version; __be64 magic; - char text[ETH_GSTRING_LEN]; }; +#ifdef CONFIG_INET +/* loopback test */ +#define MLX5E_TEST_PKT_SIZE (sizeof(struct ethhdr) + sizeof(struct iphdr) +\ + sizeof(struct udphdr) + sizeof(struct mlx5ehdr)) +#define MLX5E_TEST_MAGIC 0x5AEED15C001ULL + static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv) { struct sk_buff *skb = NULL; @@ -117,10 +116,7 @@ static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv) struct ethhdr *ethh; struct udphdr *udph; struct iphdr *iph; - int datalen, iplen; - - datalen = MLX5E_TEST_PKT_SIZE - - (sizeof(*ethh) + sizeof(*iph) + sizeof(*udph)); + int iplen; skb = netdev_alloc_skb(priv->netdev, MLX5E_TEST_PKT_SIZE); if (!skb) { @@ -149,7 +145,7 @@ static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv) /* Fill UDP header */ udph->source = htons(9); udph->dest = htons(9); /* Discard Protocol */ - udph->len = htons(datalen + sizeof(struct udphdr)); + udph->len = htons(sizeof(struct mlx5ehdr) + sizeof(struct udphdr)); udph->check = 0; /* Fill IP header */ @@ -157,7 +153,8 @@ static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv) iph->ttl = 32; iph->version = 4; iph->protocol = IPPROTO_UDP; - iplen = sizeof(struct iphdr) + sizeof(struct udphdr) + datalen; + iplen = sizeof(struct iphdr) + sizeof(struct udphdr) + + sizeof(struct mlx5ehdr); iph->tot_len = htons(iplen); iph->frag_off = 0; iph->saddr = 0; @@ -170,9 +167,6 @@ static struct sk_buff *mlx5e_test_get_udp_skb(struct mlx5e_priv *priv) mlxh = skb_put(skb, sizeof(*mlxh)); mlxh->version = 0; mlxh->magic = cpu_to_be64(MLX5E_TEST_MAGIC); - strlcpy(mlxh->text, mlx5e_test_text, sizeof(mlxh->text)); - datalen -= sizeof(*mlxh); - skb_put_zero(skb, datalen); skb->csum = 0; skb->ip_summed = CHECKSUM_PARTIAL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c index 1e55b9c27ffc..3e99d0728b2f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.c @@ -83,6 +83,7 @@ static const struct counter_desc sw_stats_desc[] = { { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_wqe_err) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_mpwqe_filler_cqes) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_mpwqe_filler_strides) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_oversize_pkts_sw_drop) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_buff_alloc_err) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cqe_compress_blks) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cqe_compress_pkts) }, @@ -161,6 +162,7 @@ void mlx5e_grp_sw_update_stats(struct mlx5e_priv *priv) s->rx_wqe_err += rq_stats->wqe_err; s->rx_mpwqe_filler_cqes += rq_stats->mpwqe_filler_cqes; s->rx_mpwqe_filler_strides += rq_stats->mpwqe_filler_strides; + s->rx_oversize_pkts_sw_drop += rq_stats->oversize_pkts_sw_drop; s->rx_buff_alloc_err += rq_stats->buff_alloc_err; s->rx_cqe_compress_blks += rq_stats->cqe_compress_blks; s->rx_cqe_compress_pkts += rq_stats->cqe_compress_pkts; @@ -1189,6 +1191,7 @@ static const struct counter_desc rq_stats_desc[] = { { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, wqe_err) }, { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, mpwqe_filler_cqes) }, { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, mpwqe_filler_strides) }, + { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, oversize_pkts_sw_drop) }, { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, buff_alloc_err) }, { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cqe_compress_blks) }, { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cqe_compress_pkts) }, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index 77f74ce11280..3f8e870ef4c9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -96,6 +96,7 @@ struct mlx5e_sw_stats { u64 rx_wqe_err; u64 rx_mpwqe_filler_cqes; u64 rx_mpwqe_filler_strides; + u64 rx_oversize_pkts_sw_drop; u64 rx_buff_alloc_err; u64 rx_cqe_compress_blks; u64 rx_cqe_compress_pkts; @@ -193,6 +194,7 @@ struct mlx5e_rq_stats { u64 wqe_err; u64 mpwqe_filler_cqes; u64 mpwqe_filler_strides; + u64 oversize_pkts_sw_drop; u64 buff_alloc_err; u64 cqe_compress_blks; u64 cqe_compress_pkts; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 608025ca5c04..fca6f4132c91 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1447,31 +1447,21 @@ static int __parse_cls_flower(struct mlx5e_priv *priv, inner_headers); } - if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { - struct flow_dissector_key_eth_addrs *key = + if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) { + struct flow_dissector_key_basic *key = skb_flow_dissector_target(f->dissector, - FLOW_DISSECTOR_KEY_ETH_ADDRS, + FLOW_DISSECTOR_KEY_BASIC, f->key); - struct flow_dissector_key_eth_addrs *mask = + struct flow_dissector_key_basic *mask = skb_flow_dissector_target(f->dissector, - FLOW_DISSECTOR_KEY_ETH_ADDRS, + FLOW_DISSECTOR_KEY_BASIC, f->mask); + MLX5_SET(fte_match_set_lyr_2_4, headers_c, ethertype, + ntohs(mask->n_proto)); + MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype, + ntohs(key->n_proto)); - ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c, - dmac_47_16), - mask->dst); - ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v, - dmac_47_16), - key->dst); - - ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c, - smac_47_16), - mask->src); - ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v, - smac_47_16), - key->src); - - if (!is_zero_ether_addr(mask->src) || !is_zero_ether_addr(mask->dst)) + if (mask->n_proto) *match_level = MLX5_MATCH_L2; } @@ -1505,9 +1495,10 @@ static int __parse_cls_flower(struct mlx5e_priv *priv, *match_level = MLX5_MATCH_L2; } - } else { + } else if (*match_level != MLX5_MATCH_NONE) { MLX5_SET(fte_match_set_lyr_2_4, headers_c, svlan_tag, 1); MLX5_SET(fte_match_set_lyr_2_4, headers_c, cvlan_tag, 1); + *match_level = MLX5_MATCH_L2; } if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CVLAN)) { @@ -1545,21 +1536,31 @@ static int __parse_cls_flower(struct mlx5e_priv *priv, } } - if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) { - struct flow_dissector_key_basic *key = + if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + struct flow_dissector_key_eth_addrs *key = skb_flow_dissector_target(f->dissector, - FLOW_DISSECTOR_KEY_BASIC, + FLOW_DISSECTOR_KEY_ETH_ADDRS, f->key); - struct flow_dissector_key_basic *mask = + struct flow_dissector_key_eth_addrs *mask = skb_flow_dissector_target(f->dissector, - FLOW_DISSECTOR_KEY_BASIC, + FLOW_DISSECTOR_KEY_ETH_ADDRS, f->mask); - MLX5_SET(fte_match_set_lyr_2_4, headers_c, ethertype, - ntohs(mask->n_proto)); - MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype, - ntohs(key->n_proto)); - if (mask->n_proto) + ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c, + dmac_47_16), + mask->dst); + ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v, + dmac_47_16), + key->dst); + + ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c, + smac_47_16), + mask->src); + ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v, + smac_47_16), + key->src); + + if (!is_zero_ether_addr(mask->src) || !is_zero_ether_addr(mask->dst)) *match_level = MLX5_MATCH_L2; } @@ -1586,10 +1587,10 @@ static int __parse_cls_flower(struct mlx5e_priv *priv, /* the HW doesn't need L3 inline to match on frag=no */ if (!(key->flags & FLOW_DIS_IS_FRAGMENT)) - *match_level = MLX5_INLINE_MODE_L2; + *match_level = MLX5_MATCH_L2; /* *** L2 attributes parsing up to here *** */ else - *match_level = MLX5_INLINE_MODE_IP; + *match_level = MLX5_MATCH_L3; } } @@ -2979,7 +2980,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, if (!actions_match_supported(priv, exts, parse_attr, flow, extack)) return -EOPNOTSUPP; - if (attr->out_count > 1 && !mlx5_esw_has_fwd_fdb(priv->mdev)) { + if (attr->mirror_count > 0 && !mlx5_esw_has_fwd_fdb(priv->mdev)) { NL_SET_ERR_MSG_MOD(extack, "current firmware doesn't support split rule for port mirroring"); netdev_warn_once(priv->netdev, "current firmware doesn't support split rule for port mirroring\n"); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c index 515e3d6de051..5a22c5874f3b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fpga/ipsec.c @@ -83,8 +83,14 @@ struct mlx5_fpga_ipsec_rule { }; static const struct rhashtable_params rhash_sa = { - .key_len = FIELD_SIZEOF(struct mlx5_fpga_ipsec_sa_ctx, hw_sa), - .key_offset = offsetof(struct mlx5_fpga_ipsec_sa_ctx, hw_sa), + /* Keep out "cmd" field from the key as it's + * value is not constant during the lifetime + * of the key object. + */ + .key_len = FIELD_SIZEOF(struct mlx5_fpga_ipsec_sa_ctx, hw_sa) - + FIELD_SIZEOF(struct mlx5_ifc_fpga_ipsec_sa_v1, cmd), + .key_offset = offsetof(struct mlx5_fpga_ipsec_sa_ctx, hw_sa) + + FIELD_SIZEOF(struct mlx5_ifc_fpga_ipsec_sa_v1, cmd), .head_offset = offsetof(struct mlx5_fpga_ipsec_sa_ctx, hash), .automatic_shrinking = true, .min_size = 1, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c index b59953daf8b4..11dabd62e2c7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c @@ -560,9 +560,9 @@ static int mlx5i_close(struct net_device *netdev) netif_carrier_off(epriv->netdev); mlx5_fs_remove_rx_underlay_qpn(mdev, ipriv->qp.qpn); - mlx5i_uninit_underlay_qp(epriv); mlx5e_deactivate_priv_channels(epriv); mlx5e_close_channels(&epriv->channels); + mlx5i_uninit_underlay_qp(epriv); unlock: mutex_unlock(&epriv->state_lock); return 0; diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c index 8e8fa823d611..69966dfc6e3d 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c @@ -191,7 +191,7 @@ qed_dcbx_dp_protocol(struct qed_hwfn *p_hwfn, struct qed_dcbx_results *p_data) static void qed_dcbx_set_params(struct qed_dcbx_results *p_data, struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - bool enable, u8 prio, u8 tc, + bool app_tlv, bool enable, u8 prio, u8 tc, enum dcbx_protocol_type type, enum qed_pci_personality personality) { @@ -210,7 +210,7 @@ qed_dcbx_set_params(struct qed_dcbx_results *p_data, p_data->arr[type].dont_add_vlan0 = true; /* QM reconf data */ - if (p_hwfn->hw_info.personality == personality) + if (app_tlv && p_hwfn->hw_info.personality == personality) qed_hw_info_set_offload_tc(&p_hwfn->hw_info, tc); /* Configure dcbx vlan priority in doorbell block for roce EDPM */ @@ -225,7 +225,7 @@ qed_dcbx_set_params(struct qed_dcbx_results *p_data, static void qed_dcbx_update_app_info(struct qed_dcbx_results *p_data, struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - bool enable, u8 prio, u8 tc, + bool app_tlv, bool enable, u8 prio, u8 tc, enum dcbx_protocol_type type) { enum qed_pci_personality personality; @@ -240,7 +240,7 @@ qed_dcbx_update_app_info(struct qed_dcbx_results *p_data, personality = qed_dcbx_app_update[i].personality; - qed_dcbx_set_params(p_data, p_hwfn, p_ptt, enable, + qed_dcbx_set_params(p_data, p_hwfn, p_ptt, app_tlv, enable, prio, tc, type, personality); } } @@ -319,8 +319,8 @@ qed_dcbx_process_tlv(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, enable = true; } - qed_dcbx_update_app_info(p_data, p_hwfn, p_ptt, enable, - priority, tc, type); + qed_dcbx_update_app_info(p_data, p_hwfn, p_ptt, true, + enable, priority, tc, type); } } @@ -341,7 +341,7 @@ qed_dcbx_process_tlv(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, continue; enable = (type == DCBX_PROTOCOL_ETH) ? false : !!dcbx_version; - qed_dcbx_update_app_info(p_data, p_hwfn, p_ptt, enable, + qed_dcbx_update_app_info(p_data, p_hwfn, p_ptt, false, enable, priority, tc, type); } diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 7ceb2b97538d..88a8576ca9ce 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -185,6 +185,10 @@ void qed_resc_free(struct qed_dev *cdev) qed_iscsi_free(p_hwfn); qed_ooo_free(p_hwfn); } + + if (QED_IS_RDMA_PERSONALITY(p_hwfn)) + qed_rdma_info_free(p_hwfn); + qed_iov_free(p_hwfn); qed_l2_free(p_hwfn); qed_dmae_info_free(p_hwfn); @@ -481,8 +485,16 @@ static u16 *qed_init_qm_get_idx_from_flags(struct qed_hwfn *p_hwfn, struct qed_qm_info *qm_info = &p_hwfn->qm_info; /* Can't have multiple flags set here */ - if (bitmap_weight((unsigned long *)&pq_flags, sizeof(pq_flags)) > 1) + if (bitmap_weight((unsigned long *)&pq_flags, + sizeof(pq_flags) * BITS_PER_BYTE) > 1) { + DP_ERR(p_hwfn, "requested multiple pq flags 0x%x\n", pq_flags); + goto err; + } + + if (!(qed_get_pq_flags(p_hwfn) & pq_flags)) { + DP_ERR(p_hwfn, "pq flag 0x%x is not set\n", pq_flags); goto err; + } switch (pq_flags) { case PQ_FLAGS_RLS: @@ -506,8 +518,7 @@ static u16 *qed_init_qm_get_idx_from_flags(struct qed_hwfn *p_hwfn, } err: - DP_ERR(p_hwfn, "BAD pq flags %d\n", pq_flags); - return NULL; + return &qm_info->start_pq; } /* save pq index in qm info */ @@ -531,20 +542,32 @@ u16 qed_get_cm_pq_idx_mcos(struct qed_hwfn *p_hwfn, u8 tc) { u8 max_tc = qed_init_qm_get_num_tcs(p_hwfn); + if (max_tc == 0) { + DP_ERR(p_hwfn, "pq with flag 0x%lx do not exist\n", + PQ_FLAGS_MCOS); + return p_hwfn->qm_info.start_pq; + } + if (tc > max_tc) DP_ERR(p_hwfn, "tc %d must be smaller than %d\n", tc, max_tc); - return qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_MCOS) + tc; + return qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_MCOS) + (tc % max_tc); } u16 qed_get_cm_pq_idx_vf(struct qed_hwfn *p_hwfn, u16 vf) { u16 max_vf = qed_init_qm_get_num_vfs(p_hwfn); + if (max_vf == 0) { + DP_ERR(p_hwfn, "pq with flag 0x%lx do not exist\n", + PQ_FLAGS_VFS); + return p_hwfn->qm_info.start_pq; + } + if (vf > max_vf) DP_ERR(p_hwfn, "vf %d must be smaller than %d\n", vf, max_vf); - return qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_VFS) + vf; + return qed_get_cm_pq_idx(p_hwfn, PQ_FLAGS_VFS) + (vf % max_vf); } u16 qed_get_cm_pq_idx_ofld_mtc(struct qed_hwfn *p_hwfn, u8 tc) @@ -1081,6 +1104,12 @@ int qed_resc_alloc(struct qed_dev *cdev) goto alloc_err; } + if (QED_IS_RDMA_PERSONALITY(p_hwfn)) { + rc = qed_rdma_info_alloc(p_hwfn); + if (rc) + goto alloc_err; + } + /* DMA info initialization */ rc = qed_dmae_info_alloc(p_hwfn); if (rc) @@ -2102,11 +2131,8 @@ int qed_hw_start_fastpath(struct qed_hwfn *p_hwfn) if (!p_ptt) return -EAGAIN; - /* If roce info is allocated it means roce is initialized and should - * be enabled in searcher. - */ if (p_hwfn->p_rdma_info && - p_hwfn->b_rdma_enabled_in_prs) + p_hwfn->p_rdma_info->active && p_hwfn->b_rdma_enabled_in_prs) qed_wr(p_hwfn, p_ptt, p_hwfn->rdma_prs_search_reg, 0x1); /* Re-open incoming traffic */ diff --git a/drivers/net/ethernet/qlogic/qed/qed_int.c b/drivers/net/ethernet/qlogic/qed/qed_int.c index 0f0aba793352..b22f464ea3fa 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_int.c +++ b/drivers/net/ethernet/qlogic/qed/qed_int.c @@ -992,6 +992,8 @@ static int qed_int_attentions(struct qed_hwfn *p_hwfn) */ do { index = p_sb_attn->sb_index; + /* finish reading index before the loop condition */ + dma_rmb(); attn_bits = le32_to_cpu(p_sb_attn->atten_bits); attn_acks = le32_to_cpu(p_sb_attn->atten_ack); } while (index != p_sb_attn->sb_index); diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index 35fd0db6a677..fff7f04d4525 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -1782,9 +1782,9 @@ static int qed_drain(struct qed_dev *cdev) return -EBUSY; } rc = qed_mcp_drain(hwfn, ptt); + qed_ptt_release(hwfn, ptt); if (rc) return rc; - qed_ptt_release(hwfn, ptt); } return 0; diff --git a/drivers/net/ethernet/qlogic/qed/qed_rdma.c b/drivers/net/ethernet/qlogic/qed/qed_rdma.c index 62113438c880..7873d6dfd91f 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_rdma.c +++ b/drivers/net/ethernet/qlogic/qed/qed_rdma.c @@ -140,22 +140,34 @@ static u32 qed_rdma_get_sb_id(void *p_hwfn, u32 rel_sb_id) return FEAT_NUM((struct qed_hwfn *)p_hwfn, QED_PF_L2_QUE) + rel_sb_id; } -static int qed_rdma_alloc(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - struct qed_rdma_start_in_params *params) +int qed_rdma_info_alloc(struct qed_hwfn *p_hwfn) { struct qed_rdma_info *p_rdma_info; - u32 num_cons, num_tasks; - int rc = -ENOMEM; - DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocating RDMA\n"); - - /* Allocate a struct with current pf rdma info */ p_rdma_info = kzalloc(sizeof(*p_rdma_info), GFP_KERNEL); if (!p_rdma_info) - return rc; + return -ENOMEM; + + spin_lock_init(&p_rdma_info->lock); p_hwfn->p_rdma_info = p_rdma_info; + return 0; +} + +void qed_rdma_info_free(struct qed_hwfn *p_hwfn) +{ + kfree(p_hwfn->p_rdma_info); + p_hwfn->p_rdma_info = NULL; +} + +static int qed_rdma_alloc(struct qed_hwfn *p_hwfn) +{ + struct qed_rdma_info *p_rdma_info = p_hwfn->p_rdma_info; + u32 num_cons, num_tasks; + int rc = -ENOMEM; + + DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "Allocating RDMA\n"); + if (QED_IS_IWARP_PERSONALITY(p_hwfn)) p_rdma_info->proto = PROTOCOLID_IWARP; else @@ -183,7 +195,7 @@ static int qed_rdma_alloc(struct qed_hwfn *p_hwfn, /* Allocate a struct with device params and fill it */ p_rdma_info->dev = kzalloc(sizeof(*p_rdma_info->dev), GFP_KERNEL); if (!p_rdma_info->dev) - goto free_rdma_info; + return rc; /* Allocate a struct with port params and fill it */ p_rdma_info->port = kzalloc(sizeof(*p_rdma_info->port), GFP_KERNEL); @@ -298,8 +310,6 @@ free_rdma_port: kfree(p_rdma_info->port); free_rdma_dev: kfree(p_rdma_info->dev); -free_rdma_info: - kfree(p_rdma_info); return rc; } @@ -370,8 +380,6 @@ static void qed_rdma_resc_free(struct qed_hwfn *p_hwfn) kfree(p_rdma_info->port); kfree(p_rdma_info->dev); - - kfree(p_rdma_info); } static void qed_rdma_free_tid(void *rdma_cxt, u32 itid) @@ -679,8 +687,6 @@ static int qed_rdma_setup(struct qed_hwfn *p_hwfn, DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "RDMA setup\n"); - spin_lock_init(&p_hwfn->p_rdma_info->lock); - qed_rdma_init_devinfo(p_hwfn, params); qed_rdma_init_port(p_hwfn); qed_rdma_init_events(p_hwfn, params); @@ -727,7 +733,7 @@ static int qed_rdma_stop(void *rdma_cxt) /* Disable RoCE search */ qed_wr(p_hwfn, p_ptt, p_hwfn->rdma_prs_search_reg, 0); p_hwfn->b_rdma_enabled_in_prs = false; - + p_hwfn->p_rdma_info->active = 0; qed_wr(p_hwfn, p_ptt, PRS_REG_ROCE_DEST_QP_MAX_PF, 0); ll2_ethertype_en = qed_rd(p_hwfn, p_ptt, PRS_REG_LIGHT_L2_ETHERTYPE_EN); @@ -1236,7 +1242,8 @@ qed_rdma_create_qp(void *rdma_cxt, u8 max_stats_queues; int rc; - if (!rdma_cxt || !in_params || !out_params || !p_hwfn->p_rdma_info) { + if (!rdma_cxt || !in_params || !out_params || + !p_hwfn->p_rdma_info->active) { DP_ERR(p_hwfn->cdev, "qed roce create qp failed due to NULL entry (rdma_cxt=%p, in=%p, out=%p, roce_info=?\n", rdma_cxt, in_params, out_params); @@ -1802,8 +1809,8 @@ bool qed_rdma_allocated_qps(struct qed_hwfn *p_hwfn) { bool result; - /* if rdma info has not been allocated, naturally there are no qps */ - if (!p_hwfn->p_rdma_info) + /* if rdma wasn't activated yet, naturally there are no qps */ + if (!p_hwfn->p_rdma_info->active) return false; spin_lock_bh(&p_hwfn->p_rdma_info->lock); @@ -1849,7 +1856,7 @@ static int qed_rdma_start(void *rdma_cxt, if (!p_ptt) goto err; - rc = qed_rdma_alloc(p_hwfn, p_ptt, params); + rc = qed_rdma_alloc(p_hwfn); if (rc) goto err1; @@ -1858,6 +1865,7 @@ static int qed_rdma_start(void *rdma_cxt, goto err2; qed_ptt_release(p_hwfn, p_ptt); + p_hwfn->p_rdma_info->active = 1; return rc; diff --git a/drivers/net/ethernet/qlogic/qed/qed_rdma.h b/drivers/net/ethernet/qlogic/qed/qed_rdma.h index 6f722ee8ee94..3689fe3e5935 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_rdma.h +++ b/drivers/net/ethernet/qlogic/qed/qed_rdma.h @@ -102,6 +102,7 @@ struct qed_rdma_info { u16 max_queue_zones; enum protocol_type proto; struct qed_iwarp_info iwarp; + u8 active:1; }; struct qed_rdma_qp { @@ -176,10 +177,14 @@ struct qed_rdma_qp { #if IS_ENABLED(CONFIG_QED_RDMA) void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); void qed_rdma_dpm_conf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); +int qed_rdma_info_alloc(struct qed_hwfn *p_hwfn); +void qed_rdma_info_free(struct qed_hwfn *p_hwfn); #else static inline void qed_rdma_dpm_conf(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) {} static inline void qed_rdma_dpm_bar(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) {} +static inline int qed_rdma_info_alloc(struct qed_hwfn *p_hwfn) {return -EINVAL;} +static inline void qed_rdma_info_free(struct qed_hwfn *p_hwfn) {} #endif int diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 33265747bf39..0fbcedcdf6e2 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -63,7 +63,7 @@ static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir) * assume the pin serves as pull-up. If direction is * output, the default value is high. */ - gpiod_set_value(bitbang->mdo, 1); + gpiod_set_value_cansleep(bitbang->mdo, 1); return; } @@ -78,7 +78,7 @@ static int mdio_get(struct mdiobb_ctrl *ctrl) struct mdio_gpio_info *bitbang = container_of(ctrl, struct mdio_gpio_info, ctrl); - return gpiod_get_value(bitbang->mdio); + return gpiod_get_value_cansleep(bitbang->mdio); } static void mdio_set(struct mdiobb_ctrl *ctrl, int what) @@ -87,9 +87,9 @@ static void mdio_set(struct mdiobb_ctrl *ctrl, int what) container_of(ctrl, struct mdio_gpio_info, ctrl); if (bitbang->mdo) - gpiod_set_value(bitbang->mdo, what); + gpiod_set_value_cansleep(bitbang->mdo, what); else - gpiod_set_value(bitbang->mdio, what); + gpiod_set_value_cansleep(bitbang->mdio, what); } static void mdc_set(struct mdiobb_ctrl *ctrl, int what) @@ -97,7 +97,7 @@ static void mdc_set(struct mdiobb_ctrl *ctrl, int what) struct mdio_gpio_info *bitbang = container_of(ctrl, struct mdio_gpio_info, ctrl); - gpiod_set_value(bitbang->mdc, what); + gpiod_set_value_cansleep(bitbang->mdc, what); } static const struct mdiobb_ops mdio_gpio_ops = { diff --git a/drivers/net/phy/mscc.c b/drivers/net/phy/mscc.c index a2e59f4f6f01..7cae17517744 100644 --- a/drivers/net/phy/mscc.c +++ b/drivers/net/phy/mscc.c @@ -810,17 +810,13 @@ static int vsc85xx_default_config(struct phy_device *phydev) phydev->mdix_ctrl = ETH_TP_MDI_AUTO; mutex_lock(&phydev->lock); - rc = phy_select_page(phydev, MSCC_PHY_PAGE_EXTENDED_2); - if (rc < 0) - goto out_unlock; - reg_val = phy_read(phydev, MSCC_PHY_RGMII_CNTL); - reg_val &= ~(RGMII_RX_CLK_DELAY_MASK); - reg_val |= (RGMII_RX_CLK_DELAY_1_1_NS << RGMII_RX_CLK_DELAY_POS); - phy_write(phydev, MSCC_PHY_RGMII_CNTL, reg_val); + reg_val = RGMII_RX_CLK_DELAY_1_1_NS << RGMII_RX_CLK_DELAY_POS; + + rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_EXTENDED_2, + MSCC_PHY_RGMII_CNTL, RGMII_RX_CLK_DELAY_MASK, + reg_val); -out_unlock: - rc = phy_restore_page(phydev, rc, rc > 0 ? 0 : rc); mutex_unlock(&phydev->lock); return rc; diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index db633ae9f784..364f514d56d8 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -985,8 +985,6 @@ static void team_port_disable(struct team *team, team->en_port_count--; team_queue_override_port_del(team, port); team_adjust_ops(team); - team_notify_peers(team); - team_mcast_rejoin(team); team_lower_state_changed(port); } diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 060135ceaf0e..e244f5d7512a 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1536,6 +1536,7 @@ static void tun_rx_batched(struct tun_struct *tun, struct tun_file *tfile, if (!rx_batched || (!more && skb_queue_empty(queue))) { local_bh_disable(); + skb_record_rx_queue(skb, tfile->queue_index); netif_receive_skb(skb); local_bh_enable(); return; @@ -1555,8 +1556,11 @@ static void tun_rx_batched(struct tun_struct *tun, struct tun_file *tfile, struct sk_buff *nskb; local_bh_disable(); - while ((nskb = __skb_dequeue(&process_queue))) + while ((nskb = __skb_dequeue(&process_queue))) { + skb_record_rx_queue(nskb, tfile->queue_index); netif_receive_skb(nskb); + } + skb_record_rx_queue(skb, tfile->queue_index); netif_receive_skb(skb); local_bh_enable(); } @@ -2451,6 +2455,7 @@ build: if (!rcu_dereference(tun->steering_prog)) rxhash = __skb_get_hash_symmetric(skb); + skb_record_rx_queue(skb, tfile->queue_index); netif_receive_skb(skb); stats = get_cpu_ptr(tun->pcpu_stats); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 3e2c041d76ac..cecfd77c9f3c 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -70,7 +70,8 @@ static const unsigned long guest_offloads[] = { VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, VIRTIO_NET_F_GUEST_ECN, - VIRTIO_NET_F_GUEST_UFO + VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_GUEST_CSUM }; struct virtnet_stat_desc { @@ -2334,9 +2335,6 @@ static int virtnet_clear_guest_offloads(struct virtnet_info *vi) if (!vi->guest_offloads) return 0; - if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_CSUM)) - offloads = 1ULL << VIRTIO_NET_F_GUEST_CSUM; - return virtnet_set_guest_offloads(vi, offloads); } @@ -2346,8 +2344,6 @@ static int virtnet_restore_guest_offloads(struct virtnet_info *vi) if (!vi->guest_offloads) return 0; - if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_CSUM)) - offloads |= 1ULL << VIRTIO_NET_F_GUEST_CSUM; return virtnet_set_guest_offloads(vi, offloads); } @@ -2365,8 +2361,9 @@ static int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog, && (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO4) || virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO6) || virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ECN) || - virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO))) { - NL_SET_ERR_MSG_MOD(extack, "Can't set XDP while host is implementing LRO, disable LRO first"); + virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO) || + virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_CSUM))) { + NL_SET_ERR_MSG_MOD(extack, "Can't set XDP while host is implementing LRO/CSUM, disable LRO/CSUM first"); return -EOPNOTSUPP; } diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index a1c2801ded10..7e49342bae38 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -6867,7 +6867,7 @@ static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 bitmap; if (drop) { - if (vif->type == NL80211_IFTYPE_STATION) { + if (vif && vif->type == NL80211_IFTYPE_STATION) { bitmap = ~(1 << WMI_MGMT_TID); list_for_each_entry(arvif, &ar->arvifs, list) { if (arvif->vdev_type == WMI_VDEV_TYPE_STA) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 1e3b5f4a4cf9..f23cb2f3d296 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1251,6 +1251,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, struct ath_vif *avp = (void *)vif->drv_priv; struct ath_node *an = &avp->mcast_node; + mutex_lock(&sc->mutex); if (IS_ENABLED(CONFIG_ATH9K_TX99)) { if (sc->cur_chan->nvifs >= 1) { mutex_unlock(&sc->mutex); @@ -1259,8 +1260,6 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, sc->tx99_vif = vif; } - mutex_lock(&sc->mutex); - ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type); sc->cur_chan->nvifs++; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 230a378c26fc..7f0a5bade70a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -6005,7 +6005,8 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, * for subsequent chanspecs. */ channel->flags = IEEE80211_CHAN_NO_HT40 | - IEEE80211_CHAN_NO_80MHZ; + IEEE80211_CHAN_NO_80MHZ | + IEEE80211_CHAN_NO_160MHZ; ch.bw = BRCMU_CHAN_BW_20; cfg->d11inf.encchspec(&ch); chaninfo = ch.chspec; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c index e7584b842dce..eb5db94f5745 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c @@ -193,6 +193,9 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch) } break; case BRCMU_CHSPEC_D11AC_BW_160: + ch->bw = BRCMU_CHAN_BW_160; + ch->sb = brcmu_maskget16(ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK, + BRCMU_CHSPEC_D11AC_SB_SHIFT); switch (ch->sb) { case BRCMU_CHAN_SB_LLL: ch->control_ch_num -= CH_70MHZ_APART; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index 2439e98431ee..7492dfb6729b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -26,6 +27,7 @@ * BSD LICENSE * * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -81,7 +83,7 @@ #define ACPI_WRDS_WIFI_DATA_SIZE (ACPI_SAR_TABLE_SIZE + 2) #define ACPI_EWRD_WIFI_DATA_SIZE ((ACPI_SAR_PROFILE_NUM - 1) * \ ACPI_SAR_TABLE_SIZE + 3) -#define ACPI_WGDS_WIFI_DATA_SIZE 18 +#define ACPI_WGDS_WIFI_DATA_SIZE 19 #define ACPI_WRDD_WIFI_DATA_SIZE 2 #define ACPI_SPLC_WIFI_DATA_SIZE 2 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 6b95d0e75889..2b8b50a77990 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -154,7 +154,11 @@ void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans, const struct iwl_fw_runtime_ops *ops, void *ops_ctx, struct dentry *dbgfs_dir); -void iwl_fw_runtime_exit(struct iwl_fw_runtime *fwrt); +static inline void iwl_fw_runtime_free(struct iwl_fw_runtime *fwrt) +{ + kfree(fwrt->dump.d3_debug_data); + fwrt->dump.d3_debug_data = NULL; +} void iwl_fw_runtime_suspend(struct iwl_fw_runtime *fwrt); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index dade206d5511..2ba890445c35 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -893,7 +893,7 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) IWL_DEBUG_RADIO(mvm, "Sending GEO_TX_POWER_LIMIT\n"); BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES * ACPI_WGDS_NUM_BANDS * - ACPI_WGDS_TABLE_SIZE != ACPI_WGDS_WIFI_DATA_SIZE); + ACPI_WGDS_TABLE_SIZE + 1 != ACPI_WGDS_WIFI_DATA_SIZE); BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES > IWL_NUM_GEO_PROFILES); @@ -928,6 +928,11 @@ static int iwl_mvm_sar_get_ewrd_table(struct iwl_mvm *mvm) return -ENOENT; } +static int iwl_mvm_sar_get_wgds_table(struct iwl_mvm *mvm) +{ + return -ENOENT; +} + static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) { return 0; @@ -954,8 +959,11 @@ static int iwl_mvm_sar_init(struct iwl_mvm *mvm) IWL_DEBUG_RADIO(mvm, "WRDS SAR BIOS table invalid or unavailable. (%d)\n", ret); - /* if not available, don't fail and don't bother with EWRD */ - return 0; + /* + * If not available, don't fail and don't bother with EWRD. + * Return 1 to tell that we can't use WGDS either. + */ + return 1; } ret = iwl_mvm_sar_get_ewrd_table(mvm); @@ -968,9 +976,13 @@ static int iwl_mvm_sar_init(struct iwl_mvm *mvm) /* choose profile 1 (WRDS) as default for both chains */ ret = iwl_mvm_sar_select_profile(mvm, 1, 1); - /* if we don't have profile 0 from BIOS, just skip it */ + /* + * If we don't have profile 0 from BIOS, just skip it. This + * means that SAR Geo will not be enabled either, even if we + * have other valid profiles. + */ if (ret == -ENOENT) - return 0; + return 1; return ret; } @@ -1168,11 +1180,19 @@ int iwl_mvm_up(struct iwl_mvm *mvm) iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); ret = iwl_mvm_sar_init(mvm); - if (ret) - goto error; + if (ret == 0) { + ret = iwl_mvm_sar_geo_init(mvm); + } else if (ret > 0 && !iwl_mvm_sar_get_wgds_table(mvm)) { + /* + * If basic SAR is not available, we check for WGDS, + * which should *not* be available either. If it is + * available, issue an error, because we can't use SAR + * Geo without basic SAR. + */ + IWL_ERR(mvm, "BIOS contains WGDS but no WRDS\n"); + } - ret = iwl_mvm_sar_geo_init(mvm); - if (ret) + if (ret < 0) goto error; iwl_mvm_leds_sync(mvm); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 505b0385d800..00f831d88366 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -301,8 +301,12 @@ struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, goto out; } - if (changed) - *changed = (resp->status == MCC_RESP_NEW_CHAN_PROFILE); + if (changed) { + u32 status = le32_to_cpu(resp->status); + + *changed = (status == MCC_RESP_NEW_CHAN_PROFILE || + status == MCC_RESP_ILLEGAL); + } regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg, __le32_to_cpu(resp->n_channels), @@ -4444,10 +4448,6 @@ static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw, sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); } - if (!fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS)) - return; - /* if beacon filtering isn't on mac80211 does it anyway */ if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER)) return; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 3633f27d048a..6fc5cc1f2b5b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -539,9 +539,8 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, } IWL_DEBUG_LAR(mvm, - "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n", - status, mcc, mcc >> 8, mcc & 0xff, - !!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels); + "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') n_chans: %d\n", + status, mcc, mcc >> 8, mcc & 0xff, n_channels); exit: iwl_free_resp(&cmd); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 0e2092526fae..af3fba10abc1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -858,6 +858,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_mvm_thermal_exit(mvm); out_free: iwl_fw_flush_dump(&mvm->fwrt); + iwl_fw_runtime_free(&mvm->fwrt); if (iwlmvm_mod_params.init_dbg) return op_mode; @@ -910,6 +911,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) iwl_mvm_tof_clean(mvm); + iwl_fw_runtime_free(&mvm->fwrt); mutex_destroy(&mvm->mutex); mutex_destroy(&mvm->d0i3_suspend_mutex); diff --git a/drivers/net/wireless/mediatek/mt76/Kconfig b/drivers/net/wireless/mediatek/mt76/Kconfig index 0ccbcd7e887d..c30d8f5bbf2a 100644 --- a/drivers/net/wireless/mediatek/mt76/Kconfig +++ b/drivers/net/wireless/mediatek/mt76/Kconfig @@ -1,6 +1,12 @@ config MT76_CORE tristate +config MT76_LEDS + bool + depends on MT76_CORE + depends on LEDS_CLASS=y || MT76_CORE=LEDS_CLASS + default y + config MT76_USB tristate depends on MT76_CORE diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 2a699e8b79bf..7d219ff2d480 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -345,9 +345,11 @@ int mt76_register_device(struct mt76_dev *dev, bool vht, mt76_check_sband(dev, NL80211_BAND_2GHZ); mt76_check_sband(dev, NL80211_BAND_5GHZ); - ret = mt76_led_init(dev); - if (ret) - return ret; + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + ret = mt76_led_init(dev); + if (ret) + return ret; + } return ieee80211_register_hw(hw); } diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h index 47c42c607964..7806963b1905 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02.h +++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h @@ -71,7 +71,6 @@ struct mt76x02_dev { struct mac_address macaddr_list[8]; struct mutex phy_mutex; - struct mutex mutex; u8 txdone_seq; DECLARE_KFIFO_PTR(txstatus_fifo, struct mt76x02_tx_status); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c index 3824290b219d..fd125722d1fb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c @@ -507,8 +507,10 @@ int mt76x2_register_device(struct mt76x02_dev *dev) mt76x2_dfs_init_detector(dev); /* init led callbacks */ - dev->mt76.led_cdev.brightness_set = mt76x2_led_set_brightness; - dev->mt76.led_cdev.blink_set = mt76x2_led_set_blink; + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + dev->mt76.led_cdev.brightness_set = mt76x2_led_set_brightness; + dev->mt76.led_cdev.blink_set = mt76x2_led_set_blink; + } ret = mt76_register_device(&dev->mt76, true, mt76x02_rates, ARRAY_SIZE(mt76x02_rates)); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c index 034a06295668..3f001bd6806c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c @@ -272,9 +272,9 @@ mt76x2_set_rts_threshold(struct ieee80211_hw *hw, u32 val) if (val != ~0 && val > 0xffff) return -EINVAL; - mutex_lock(&dev->mutex); + mutex_lock(&dev->mt76.mutex); mt76x2_mac_set_tx_protection(dev, val); - mutex_unlock(&dev->mutex); + mutex_unlock(&dev->mt76.mutex); return 0; } diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c index 4c2154b9e6a3..bd10165d7eec 100644 --- a/drivers/net/wireless/ti/wlcore/sdio.c +++ b/drivers/net/wireless/ti/wlcore/sdio.c @@ -285,7 +285,7 @@ static int wl1271_probe(struct sdio_func *func, struct resource res[2]; mmc_pm_flag_t mmcflags; int ret = -ENOMEM; - int irq, wakeirq; + int irq, wakeirq, num_irqs; const char *chip_family; /* We are only able to handle the wlan function */ @@ -353,12 +353,17 @@ static int wl1271_probe(struct sdio_func *func, irqd_get_trigger_type(irq_get_irq_data(irq)); res[0].name = "irq"; - res[1].start = wakeirq; - res[1].flags = IORESOURCE_IRQ | - irqd_get_trigger_type(irq_get_irq_data(wakeirq)); - res[1].name = "wakeirq"; - ret = platform_device_add_resources(glue->core, res, ARRAY_SIZE(res)); + if (wakeirq > 0) { + res[1].start = wakeirq; + res[1].flags = IORESOURCE_IRQ | + irqd_get_trigger_type(irq_get_irq_data(wakeirq)); + res[1].name = "wakeirq"; + num_irqs = 2; + } else { + num_irqs = 1; + } + ret = platform_device_add_resources(glue->core, res, num_irqs); if (ret) { dev_err(glue->dev, "can't add resources\n"); goto out_dev_put; diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 0b70c8bab045..54032c466636 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -152,6 +152,7 @@ struct nvme_fc_ctrl { bool ioq_live; bool assoc_active; + atomic_t err_work_active; u64 association_id; struct list_head ctrl_list; /* rport->ctrl_list */ @@ -160,6 +161,7 @@ struct nvme_fc_ctrl { struct blk_mq_tag_set tag_set; struct delayed_work connect_work; + struct work_struct err_work; struct kref ref; u32 flags; @@ -1531,6 +1533,10 @@ nvme_fc_abort_aen_ops(struct nvme_fc_ctrl *ctrl) struct nvme_fc_fcp_op *aen_op = ctrl->aen_ops; int i; + /* ensure we've initialized the ops once */ + if (!(aen_op->flags & FCOP_FLAGS_AEN)) + return; + for (i = 0; i < NVME_NR_AEN_COMMANDS; i++, aen_op++) __nvme_fc_abort_op(ctrl, aen_op); } @@ -2049,7 +2055,25 @@ nvme_fc_nvme_ctrl_freed(struct nvme_ctrl *nctrl) static void nvme_fc_error_recovery(struct nvme_fc_ctrl *ctrl, char *errmsg) { - /* only proceed if in LIVE state - e.g. on first error */ + int active; + + /* + * if an error (io timeout, etc) while (re)connecting, + * it's an error on creating the new association. + * Start the error recovery thread if it hasn't already + * been started. It is expected there could be multiple + * ios hitting this path before things are cleaned up. + */ + if (ctrl->ctrl.state == NVME_CTRL_CONNECTING) { + active = atomic_xchg(&ctrl->err_work_active, 1); + if (!active && !schedule_work(&ctrl->err_work)) { + atomic_set(&ctrl->err_work_active, 0); + WARN_ON(1); + } + return; + } + + /* Otherwise, only proceed if in LIVE state - e.g. on first error */ if (ctrl->ctrl.state != NVME_CTRL_LIVE) return; @@ -2814,6 +2838,7 @@ nvme_fc_delete_ctrl(struct nvme_ctrl *nctrl) { struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl); + cancel_work_sync(&ctrl->err_work); cancel_delayed_work_sync(&ctrl->connect_work); /* * kill the association on the link side. this will block @@ -2866,23 +2891,30 @@ nvme_fc_reconnect_or_delete(struct nvme_fc_ctrl *ctrl, int status) } static void -nvme_fc_reset_ctrl_work(struct work_struct *work) +__nvme_fc_terminate_io(struct nvme_fc_ctrl *ctrl) { - struct nvme_fc_ctrl *ctrl = - container_of(work, struct nvme_fc_ctrl, ctrl.reset_work); - int ret; - - nvme_stop_ctrl(&ctrl->ctrl); + nvme_stop_keep_alive(&ctrl->ctrl); /* will block will waiting for io to terminate */ nvme_fc_delete_association(ctrl); - if (!nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) { + if (ctrl->ctrl.state != NVME_CTRL_CONNECTING && + !nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_CONNECTING)) dev_err(ctrl->ctrl.device, "NVME-FC{%d}: error_recovery: Couldn't change state " "to CONNECTING\n", ctrl->cnum); - return; - } +} + +static void +nvme_fc_reset_ctrl_work(struct work_struct *work) +{ + struct nvme_fc_ctrl *ctrl = + container_of(work, struct nvme_fc_ctrl, ctrl.reset_work); + int ret; + + __nvme_fc_terminate_io(ctrl); + + nvme_stop_ctrl(&ctrl->ctrl); if (ctrl->rport->remoteport.port_state == FC_OBJSTATE_ONLINE) ret = nvme_fc_create_association(ctrl); @@ -2897,6 +2929,24 @@ nvme_fc_reset_ctrl_work(struct work_struct *work) ctrl->cnum); } +static void +nvme_fc_connect_err_work(struct work_struct *work) +{ + struct nvme_fc_ctrl *ctrl = + container_of(work, struct nvme_fc_ctrl, err_work); + + __nvme_fc_terminate_io(ctrl); + + atomic_set(&ctrl->err_work_active, 0); + + /* + * Rescheduling the connection after recovering + * from the io error is left to the reconnect work + * item, which is what should have stalled waiting on + * the io that had the error that scheduled this work. + */ +} + static const struct nvme_ctrl_ops nvme_fc_ctrl_ops = { .name = "fc", .module = THIS_MODULE, @@ -3007,6 +3057,7 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, ctrl->cnum = idx; ctrl->ioq_live = false; ctrl->assoc_active = false; + atomic_set(&ctrl->err_work_active, 0); init_waitqueue_head(&ctrl->ioabort_wait); get_device(ctrl->dev); @@ -3014,6 +3065,7 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, INIT_WORK(&ctrl->ctrl.reset_work, nvme_fc_reset_ctrl_work); INIT_DELAYED_WORK(&ctrl->connect_work, nvme_fc_connect_ctrl_work); + INIT_WORK(&ctrl->err_work, nvme_fc_connect_err_work); spin_lock_init(&ctrl->lock); /* io queue count */ @@ -3103,6 +3155,7 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts, fail_ctrl: nvme_change_ctrl_state(&ctrl->ctrl, NVME_CTRL_DELETING); cancel_work_sync(&ctrl->ctrl.reset_work); + cancel_work_sync(&ctrl->err_work); cancel_delayed_work_sync(&ctrl->connect_work); ctrl->ctrl.opts = NULL; diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 9b18ce90f907..27f67dfa649d 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -44,6 +44,7 @@ struct nvmem_cell { int bytes; int bit_offset; int nbits; + struct device_node *np; struct nvmem_device *nvmem; struct list_head node; }; @@ -298,6 +299,7 @@ static void nvmem_cell_drop(struct nvmem_cell *cell) mutex_lock(&nvmem_mutex); list_del(&cell->node); mutex_unlock(&nvmem_mutex); + of_node_put(cell->np); kfree(cell->name); kfree(cell); } @@ -530,6 +532,7 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem) return -ENOMEM; cell->nvmem = nvmem; + cell->np = of_node_get(child); cell->offset = be32_to_cpup(addr++); cell->bytes = be32_to_cpup(addr); cell->name = kasprintf(GFP_KERNEL, "%pOFn", child); @@ -960,14 +963,13 @@ out: #if IS_ENABLED(CONFIG_OF) static struct nvmem_cell * -nvmem_find_cell_by_index(struct nvmem_device *nvmem, int index) +nvmem_find_cell_by_node(struct nvmem_device *nvmem, struct device_node *np) { struct nvmem_cell *cell = NULL; - int i = 0; mutex_lock(&nvmem_mutex); list_for_each_entry(cell, &nvmem->cells, node) { - if (index == i++) + if (np == cell->np) break; } mutex_unlock(&nvmem_mutex); @@ -1011,7 +1013,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) if (IS_ERR(nvmem)) return ERR_CAST(nvmem); - cell = nvmem_find_cell_by_index(nvmem, index); + cell = nvmem_find_cell_by_node(nvmem, cell_np); if (!cell) { __nvmem_device_put(nvmem); return ERR_PTR(-ENOENT); diff --git a/drivers/opp/ti-opp-supply.c b/drivers/opp/ti-opp-supply.c index 9e5a9a3112c9..3f4fb4dbbe33 100644 --- a/drivers/opp/ti-opp-supply.c +++ b/drivers/opp/ti-opp-supply.c @@ -288,7 +288,10 @@ static int ti_opp_supply_set_opp(struct dev_pm_set_opp_data *data) int ret; vdd_uv = _get_optimal_vdd_voltage(dev, &opp_data, - new_supply_vbb->u_volt); + new_supply_vdd->u_volt); + + if (new_supply_vdd->u_volt_min < vdd_uv) + new_supply_vdd->u_volt_min = vdd_uv; /* Scaling up? Scale voltage before frequency */ if (freq > old_freq) { diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index f96ec68af2e5..dcbf5c857743 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -415,9 +415,9 @@ static irqreturn_t ism_handle_irq(int irq, void *data) break; clear_bit_inv(bit, bv); + ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET] = 0; barrier(); smcd_handle_irq(ism->smcd, bit + ISM_DMB_BIT_OFFSET); - ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET] = 0; } if (ism->sba->e) { diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 20c85eed1a75..b658b9a5eb1e 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1749,7 +1749,7 @@ qla2x00_loop_reset(scsi_qla_host_t *vha) static void __qla2x00_abort_all_cmds(struct qla_qpair *qp, int res) { - int cnt; + int cnt, status; unsigned long flags; srb_t *sp; scsi_qla_host_t *vha = qp->vha; @@ -1799,10 +1799,16 @@ __qla2x00_abort_all_cmds(struct qla_qpair *qp, int res) if (!sp_get(sp)) { spin_unlock_irqrestore (qp->qp_lock_ptr, flags); - qla2xxx_eh_abort( + status = qla2xxx_eh_abort( GET_CMD_SP(sp)); spin_lock_irqsave (qp->qp_lock_ptr, flags); + /* + * Get rid of extra reference caused + * by early exit from qla2xxx_eh_abort + */ + if (status == FAST_IO_FAIL) + atomic_dec(&sp->ref_count); } } sp->done(sp, res); diff --git a/drivers/scsi/ufs/ufs-hisi.c b/drivers/scsi/ufs/ufs-hisi.c index 46df707e6f2c..452e19f8fb47 100644 --- a/drivers/scsi/ufs/ufs-hisi.c +++ b/drivers/scsi/ufs/ufs-hisi.c @@ -20,6 +20,7 @@ #include "unipro.h" #include "ufs-hisi.h" #include "ufshci.h" +#include "ufs_quirks.h" static int ufs_hisi_check_hibern8(struct ufs_hba *hba) { @@ -390,6 +391,14 @@ static void ufs_hisi_set_dev_cap(struct ufs_hisi_dev_params *hisi_param) static void ufs_hisi_pwr_change_pre_change(struct ufs_hba *hba) { + if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_VS_DEBUGSAVECONFIGTIME) { + pr_info("ufs flash device must set VS_DebugSaveConfigTime 0x10\n"); + /* VS_DebugSaveConfigTime */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0xD0A0), 0x10); + /* sync length */ + ufshcd_dme_set(hba, UIC_ARG_MIB(0x1556), 0x48); + } + /* update */ ufshcd_dme_set(hba, UIC_ARG_MIB(0x15A8), 0x1); /* PA_TxSkip */ diff --git a/drivers/scsi/ufs/ufs_quirks.h b/drivers/scsi/ufs/ufs_quirks.h index 71f73d1d1ad1..5d2dfdb41a6f 100644 --- a/drivers/scsi/ufs/ufs_quirks.h +++ b/drivers/scsi/ufs/ufs_quirks.h @@ -131,4 +131,10 @@ struct ufs_dev_fix { */ #define UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME (1 << 8) +/* + * Some UFS devices require VS_DebugSaveConfigTime is 0x10, + * enabling this quirk ensure this. + */ +#define UFS_DEVICE_QUIRK_HOST_VS_DEBUGSAVECONFIGTIME (1 << 9) + #endif /* UFS_QUIRKS_H_ */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 27db55b0ca7f..f1c57cd33b5b 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -231,6 +231,8 @@ static struct ufs_dev_fix ufs_fixups[] = { UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ), UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL, UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME), + UFS_FIX(UFS_VENDOR_SKHYNIX, "hB8aL1" /*H28U62301AMR*/, + UFS_DEVICE_QUIRK_HOST_VS_DEBUGSAVECONFIGTIME), END_FIX }; diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c index 7218fb963d0a..1382a8df6c75 100644 --- a/drivers/slimbus/qcom-ngd-ctrl.c +++ b/drivers/slimbus/qcom-ngd-ctrl.c @@ -777,9 +777,6 @@ static int qcom_slim_ngd_xfer_msg(struct slim_controller *sctrl, u8 la = txn->la; bool usr_msg = false; - if (txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG) - return -EPROTONOSUPPORT; - if (txn->mt == SLIM_MSG_MT_CORE && (txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION && txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW)) diff --git a/drivers/slimbus/slimbus.h b/drivers/slimbus/slimbus.h index 4399d1873e2d..9be41089edde 100644 --- a/drivers/slimbus/slimbus.h +++ b/drivers/slimbus/slimbus.h @@ -61,12 +61,6 @@ #define SLIM_MSG_MC_NEXT_REMOVE_CHANNEL 0x58 #define SLIM_MSG_MC_RECONFIGURE_NOW 0x5F -/* - * Clock pause flag to indicate that the reconfig message - * corresponds to clock pause sequence - */ -#define SLIM_MSG_CLK_PAUSE_SEQ_FLG (1U << 8) - /* Clock pause values per SLIMbus spec */ #define SLIM_CLK_FAST 0 #define SLIM_CLK_CONST_PHASE 1 diff --git a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c index a53231b08d30..e3425bf082ae 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_ipipeif.c @@ -310,6 +310,7 @@ static int ipipeif_hw_setup(struct v4l2_subdev *sd) ipipeif_write(val, ipipeif_base_addr, IPIPEIF_CFG2); break; } + /* fall through */ case IPIPEIF_SDRAM_YUV: /* Set clock divider */ diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c index 82558455384a..dd121f66fa2d 100644 --- a/drivers/staging/media/sunxi/cedrus/cedrus.c +++ b/drivers/staging/media/sunxi/cedrus/cedrus.c @@ -253,7 +253,7 @@ static const struct v4l2_m2m_ops cedrus_m2m_ops = { static const struct media_device_ops cedrus_m2m_media_ops = { .req_validate = cedrus_request_validate, - .req_queue = vb2_m2m_request_queue, + .req_queue = v4l2_m2m_request_queue, }; static int cedrus_probe(struct platform_device *pdev) diff --git a/drivers/staging/vboxvideo/vbox_drv.c b/drivers/staging/vboxvideo/vbox_drv.c index 257030460fb6..d3e23dd70c1b 100644 --- a/drivers/staging/vboxvideo/vbox_drv.c +++ b/drivers/staging/vboxvideo/vbox_drv.c @@ -279,7 +279,6 @@ static struct drm_driver driver = { .gem_free_object_unlocked = vbox_gem_free_object, .dumb_create = vbox_dumb_create, .dumb_map_offset = vbox_dumb_mmap_offset, - .dumb_destroy = drm_gem_dumb_destroy, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_export = drm_gem_prime_export, diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 85644669fbe7..0a357db4b31b 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -961,6 +961,8 @@ int __uio_register_device(struct module *owner, if (ret) goto err_uio_dev_add_attributes; + info->uio_dev = idev; + if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) { /* * Note that we deliberately don't use devm_request_irq @@ -972,11 +974,12 @@ int __uio_register_device(struct module *owner, */ ret = request_irq(info->irq, uio_interrupt, info->irq_flags, info->name, idev); - if (ret) + if (ret) { + info->uio_dev = NULL; goto err_request_irq; + } } - info->uio_dev = idev; return 0; err_request_irq: diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 47d75c20c211..1b68fed464cb 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1696,6 +1696,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */ .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ }, + { USB_DEVICE(0x0572, 0x1349), /* Hiro (Conexant) USB MODEM H50228 */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, { USB_DEVICE(0x20df, 0x0001), /* Simtec Electronics Entropy Key */ .driver_info = QUIRK_CONTROL_LINE_STATE, }, { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index c6077d582d29..0f9381b69a3b 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2794,6 +2794,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1, int i, status; u16 portchange, portstatus; struct usb_port *port_dev = hub->ports[port1 - 1]; + int reset_recovery_time; if (!hub_is_superspeed(hub->hdev)) { if (warm) { @@ -2849,7 +2850,9 @@ static int hub_port_reset(struct usb_hub *hub, int port1, USB_PORT_FEAT_C_BH_PORT_RESET); usb_clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_PORT_LINK_STATE); - usb_clear_port_feature(hub->hdev, port1, + + if (udev) + usb_clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_C_CONNECTION); /* @@ -2885,11 +2888,18 @@ static int hub_port_reset(struct usb_hub *hub, int port1, done: if (status == 0) { - /* TRSTRCY = 10 ms; plus some extra */ if (port_dev->quirks & USB_PORT_QUIRK_FAST_ENUM) usleep_range(10000, 12000); - else - msleep(10 + 40); + else { + /* TRSTRCY = 10 ms; plus some extra */ + reset_recovery_time = 10 + 40; + + /* Hub needs extra delay after resetting its port. */ + if (hub->hdev->quirks & USB_QUIRK_HUB_SLOW_RESET) + reset_recovery_time += 100; + + msleep(reset_recovery_time); + } if (udev) { struct usb_hcd *hcd = bus_to_hcd(udev->bus); diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 178d6c6063c0..f9ff03e6af93 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -128,6 +128,9 @@ static int quirks_param_set(const char *val, const struct kernel_param *kp) case 'n': flags |= USB_QUIRK_DELAY_CTRL_MSG; break; + case 'o': + flags |= USB_QUIRK_HUB_SLOW_RESET; + break; /* Ignore unrecognized flag characters */ } } @@ -380,6 +383,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x1a0a, 0x0200), .driver_info = USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL }, + /* Terminus Technology Inc. Hub */ + { USB_DEVICE(0x1a40, 0x0101), .driver_info = USB_QUIRK_HUB_SLOW_RESET }, + /* Corsair K70 RGB */ { USB_DEVICE(0x1b1c, 0x1b13), .driver_info = USB_QUIRK_DELAY_INIT }, @@ -391,6 +397,9 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x1b1c, 0x1b20), .driver_info = USB_QUIRK_DELAY_INIT | USB_QUIRK_DELAY_CTRL_MSG }, + /* Corsair K70 LUX RGB */ + { USB_DEVICE(0x1b1c, 0x1b33), .driver_info = USB_QUIRK_DELAY_INIT }, + /* Corsair K70 LUX */ { USB_DEVICE(0x1b1c, 0x1b36), .driver_info = USB_QUIRK_DELAY_INIT }, @@ -411,6 +420,11 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x2040, 0x7200), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, + /* Raydium Touchscreen */ + { USB_DEVICE(0x2386, 0x3114), .driver_info = USB_QUIRK_NO_LPM }, + + { USB_DEVICE(0x2386, 0x3119), .driver_info = USB_QUIRK_NO_LPM }, + /* DJI CineSSD */ { USB_DEVICE(0x2ca3, 0x0031), .driver_info = USB_QUIRK_NO_LPM }, diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c index d257c541e51b..7afc10872f1f 100644 --- a/drivers/usb/dwc2/pci.c +++ b/drivers/usb/dwc2/pci.c @@ -120,6 +120,7 @@ static int dwc2_pci_probe(struct pci_dev *pci, dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO); if (!dwc2) { dev_err(dev, "couldn't allocate dwc2 device\n"); + ret = -ENOMEM; goto err; } diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index becfbb87f791..2f2048aa5fde 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1499,6 +1499,7 @@ static int dwc3_probe(struct platform_device *pdev) err5: dwc3_event_buffers_cleanup(dwc); + dwc3_ulpi_exit(dwc); err4: dwc3_free_scratch_buffers(dwc); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 1286076a8890..842795856bf4 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -283,8 +283,10 @@ err: static void dwc3_pci_remove(struct pci_dev *pci) { struct dwc3_pci *dwc = pci_get_drvdata(pci); + struct pci_dev *pdev = dwc->pci; - gpiod_remove_lookup_table(&platform_bytcr_gpios); + if (pdev->device == PCI_DEVICE_ID_INTEL_BYT) + gpiod_remove_lookup_table(&platform_bytcr_gpios); #ifdef CONFIG_PM cancel_work_sync(&dwc->wakeup_work); #endif diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 679c12e14522..9faad896b3a1 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1081,7 +1081,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, /* Now prepare one extra TRB to align transfer size */ trb = &dep->trb_pool[dep->trb_enqueue]; __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, - maxp - rem, false, 0, + maxp - rem, false, 1, req->request.stream_id, req->request.short_not_ok, req->request.no_interrupt); @@ -1125,7 +1125,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, /* Now prepare one extra TRB to align transfer size */ trb = &dep->trb_pool[dep->trb_enqueue]; __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem, - false, 0, req->request.stream_id, + false, 1, req->request.stream_id, req->request.short_not_ok, req->request.no_interrupt); } else if (req->request.zero && req->request.length && @@ -1141,7 +1141,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, /* Now prepare one extra TRB to handle ZLP */ trb = &dep->trb_pool[dep->trb_enqueue]; __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0, - false, 0, req->request.stream_id, + false, 1, req->request.stream_id, req->request.short_not_ok, req->request.no_interrupt); } else { @@ -2259,7 +2259,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep, * with one TRB pending in the ring. We need to manually clear HWO bit * from that TRB. */ - if ((req->zero || req->unaligned) && (trb->ctrl & DWC3_TRB_CTRL_HWO)) { + if ((req->zero || req->unaligned) && !(trb->ctrl & DWC3_TRB_CTRL_CHN)) { trb->ctrl &= ~DWC3_TRB_CTRL_HWO; return 1; } diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 3ada83d81bda..31e8bf3578c8 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -215,7 +215,6 @@ struct ffs_io_data { struct mm_struct *mm; struct work_struct work; - struct work_struct cancellation_work; struct usb_ep *ep; struct usb_request *req; @@ -1073,31 +1072,22 @@ ffs_epfile_open(struct inode *inode, struct file *file) return 0; } -static void ffs_aio_cancel_worker(struct work_struct *work) -{ - struct ffs_io_data *io_data = container_of(work, struct ffs_io_data, - cancellation_work); - - ENTER(); - - usb_ep_dequeue(io_data->ep, io_data->req); -} - static int ffs_aio_cancel(struct kiocb *kiocb) { struct ffs_io_data *io_data = kiocb->private; - struct ffs_data *ffs = io_data->ffs; + struct ffs_epfile *epfile = kiocb->ki_filp->private_data; int value; ENTER(); - if (likely(io_data && io_data->ep && io_data->req)) { - INIT_WORK(&io_data->cancellation_work, ffs_aio_cancel_worker); - queue_work(ffs->io_completion_wq, &io_data->cancellation_work); - value = -EINPROGRESS; - } else { + spin_lock_irq(&epfile->ffs->eps_lock); + + if (likely(io_data && io_data->ep && io_data->req)) + value = usb_ep_dequeue(io_data->ep, io_data->req); + else value = -EINVAL; - } + + spin_unlock_irq(&epfile->ffs->eps_lock); return value; } diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c index 27f00160332e..3c4abb5a1c3f 100644 --- a/drivers/usb/host/xhci-histb.c +++ b/drivers/usb/host/xhci-histb.c @@ -325,14 +325,16 @@ static int xhci_histb_remove(struct platform_device *dev) struct xhci_hcd_histb *histb = platform_get_drvdata(dev); struct usb_hcd *hcd = histb->hcd; struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct usb_hcd *shared_hcd = xhci->shared_hcd; xhci->xhc_state |= XHCI_STATE_REMOVING; - usb_remove_hcd(xhci->shared_hcd); + usb_remove_hcd(shared_hcd); + xhci->shared_hcd = NULL; device_wakeup_disable(&dev->dev); usb_remove_hcd(hcd); - usb_put_hcd(xhci->shared_hcd); + usb_put_hcd(shared_hcd); xhci_histb_host_disable(histb); usb_put_hcd(hcd); diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 12eea73d9f20..94aca1b5ac8a 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -876,7 +876,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, status |= USB_PORT_STAT_SUSPEND; } if ((raw_port_status & PORT_PLS_MASK) == XDEV_RESUME && - !DEV_SUPERSPEED_ANY(raw_port_status)) { + !DEV_SUPERSPEED_ANY(raw_port_status) && hcd->speed < HCD_USB3) { if ((raw_port_status & PORT_RESET) || !(raw_port_status & PORT_PE)) return 0xffffffff; @@ -921,7 +921,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, time_left = wait_for_completion_timeout( &bus_state->rexit_done[wIndex], msecs_to_jiffies( - XHCI_MAX_REXIT_TIMEOUT)); + XHCI_MAX_REXIT_TIMEOUT_MS)); spin_lock_irqsave(&xhci->lock, flags); if (time_left) { @@ -935,7 +935,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, } else { int port_status = readl(port->addr); xhci_warn(xhci, "Port resume took longer than %i msec, port status = 0x%x\n", - XHCI_MAX_REXIT_TIMEOUT, + XHCI_MAX_REXIT_TIMEOUT_MS, port_status); status |= USB_PORT_STAT_SUSPEND; clear_bit(wIndex, &bus_state->rexit_ports); @@ -1474,15 +1474,18 @@ int xhci_bus_suspend(struct usb_hcd *hcd) unsigned long flags; struct xhci_hub *rhub; struct xhci_port **ports; + u32 portsc_buf[USB_MAXCHILDREN]; + bool wake_enabled; rhub = xhci_get_rhub(hcd); ports = rhub->ports; max_ports = rhub->num_ports; bus_state = &xhci->bus_state[hcd_index(hcd)]; + wake_enabled = hcd->self.root_hub->do_remote_wakeup; spin_lock_irqsave(&xhci->lock, flags); - if (hcd->self.root_hub->do_remote_wakeup) { + if (wake_enabled) { if (bus_state->resuming_ports || /* USB2 */ bus_state->port_remote_wakeup) { /* USB3 */ spin_unlock_irqrestore(&xhci->lock, flags); @@ -1490,26 +1493,36 @@ int xhci_bus_suspend(struct usb_hcd *hcd) return -EBUSY; } } - - port_index = max_ports; + /* + * Prepare ports for suspend, but don't write anything before all ports + * are checked and we know bus suspend can proceed + */ bus_state->bus_suspended = 0; + port_index = max_ports; while (port_index--) { - /* suspend the port if the port is not suspended */ u32 t1, t2; - int slot_id; t1 = readl(ports[port_index]->addr); t2 = xhci_port_state_to_neutral(t1); + portsc_buf[port_index] = 0; - if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) { - xhci_dbg(xhci, "port %d not suspended\n", port_index); - slot_id = xhci_find_slot_id_by_port(hcd, xhci, - port_index + 1); - if (slot_id) { + /* Bail out if a USB3 port has a new device in link training */ + if ((t1 & PORT_PLS_MASK) == XDEV_POLLING) { + bus_state->bus_suspended = 0; + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_dbg(xhci, "Bus suspend bailout, port in polling\n"); + return -EBUSY; + } + + /* suspend ports in U0, or bail out for new connect changes */ + if ((t1 & PORT_PE) && (t1 & PORT_PLS_MASK) == XDEV_U0) { + if ((t1 & PORT_CSC) && wake_enabled) { + bus_state->bus_suspended = 0; spin_unlock_irqrestore(&xhci->lock, flags); - xhci_stop_device(xhci, slot_id, 1); - spin_lock_irqsave(&xhci->lock, flags); + xhci_dbg(xhci, "Bus suspend bailout, port connect change\n"); + return -EBUSY; } + xhci_dbg(xhci, "port %d not suspended\n", port_index); t2 &= ~PORT_PLS_MASK; t2 |= PORT_LINK_STROBE | XDEV_U3; set_bit(port_index, &bus_state->bus_suspended); @@ -1518,7 +1531,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) * including the USB 3.0 roothub, but only if CONFIG_PM * is enabled, so also enable remote wake here. */ - if (hcd->self.root_hub->do_remote_wakeup) { + if (wake_enabled) { if (t1 & PORT_CONNECT) { t2 |= PORT_WKOC_E | PORT_WKDISC_E; t2 &= ~PORT_WKCONN_E; @@ -1538,7 +1551,26 @@ int xhci_bus_suspend(struct usb_hcd *hcd) t1 = xhci_port_state_to_neutral(t1); if (t1 != t2) - writel(t2, ports[port_index]->addr); + portsc_buf[port_index] = t2; + } + + /* write port settings, stopping and suspending ports if needed */ + port_index = max_ports; + while (port_index--) { + if (!portsc_buf[port_index]) + continue; + if (test_bit(port_index, &bus_state->bus_suspended)) { + int slot_id; + + slot_id = xhci_find_slot_id_by_port(hcd, xhci, + port_index + 1); + if (slot_id) { + spin_unlock_irqrestore(&xhci->lock, flags); + xhci_stop_device(xhci, slot_id, 1); + spin_lock_irqsave(&xhci->lock, flags); + } + } + writel(portsc_buf[port_index], ports[port_index]->addr); } hcd->state = HC_STATE_SUSPENDED; bus_state->next_statechange = jiffies + msecs_to_jiffies(10); diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 71d0d33c3286..60987c787e44 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -590,12 +590,14 @@ static int xhci_mtk_remove(struct platform_device *dev) struct xhci_hcd_mtk *mtk = platform_get_drvdata(dev); struct usb_hcd *hcd = mtk->hcd; struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct usb_hcd *shared_hcd = xhci->shared_hcd; - usb_remove_hcd(xhci->shared_hcd); + usb_remove_hcd(shared_hcd); + xhci->shared_hcd = NULL; device_init_wakeup(&dev->dev, false); usb_remove_hcd(hcd); - usb_put_hcd(xhci->shared_hcd); + usb_put_hcd(shared_hcd); usb_put_hcd(hcd); xhci_mtk_sch_exit(mtk); xhci_mtk_clks_disable(mtk); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 01c57055c0c5..a9515265db4d 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -248,6 +248,11 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_TI && pdev->device == 0x8241) xhci->quirks |= XHCI_LIMIT_ENDPOINT_INTERVAL_7; + if ((pdev->vendor == PCI_VENDOR_ID_BROADCOM || + pdev->vendor == PCI_VENDOR_ID_CAVIUM) && + pdev->device == 0x9026) + xhci->quirks |= XHCI_RESET_PLL_ON_DISCONNECT; + if (xhci->quirks & XHCI_RESET_ON_RESUME) xhci_dbg_trace(xhci, trace_xhci_dbg_quirks, "QUIRK: Resetting on resume"); @@ -380,6 +385,7 @@ static void xhci_pci_remove(struct pci_dev *dev) if (xhci->shared_hcd) { usb_remove_hcd(xhci->shared_hcd); usb_put_hcd(xhci->shared_hcd); + xhci->shared_hcd = NULL; } /* Workaround for spurious wakeups at shutdown with HSW */ diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 32b5574ad5c5..ef09cb06212f 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -362,14 +362,16 @@ static int xhci_plat_remove(struct platform_device *dev) struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct clk *clk = xhci->clk; struct clk *reg_clk = xhci->reg_clk; + struct usb_hcd *shared_hcd = xhci->shared_hcd; xhci->xhc_state |= XHCI_STATE_REMOVING; - usb_remove_hcd(xhci->shared_hcd); + usb_remove_hcd(shared_hcd); + xhci->shared_hcd = NULL; usb_phy_shutdown(hcd->usb_phy); usb_remove_hcd(hcd); - usb_put_hcd(xhci->shared_hcd); + usb_put_hcd(shared_hcd); clk_disable_unprepare(clk); clk_disable_unprepare(reg_clk); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index a8d92c90fb58..65750582133f 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1521,6 +1521,35 @@ static void handle_device_notification(struct xhci_hcd *xhci, usb_wakeup_notification(udev->parent, udev->portnum); } +/* + * Quirk hanlder for errata seen on Cavium ThunderX2 processor XHCI + * Controller. + * As per ThunderX2errata-129 USB 2 device may come up as USB 1 + * If a connection to a USB 1 device is followed by another connection + * to a USB 2 device. + * + * Reset the PHY after the USB device is disconnected if device speed + * is less than HCD_USB3. + * Retry the reset sequence max of 4 times checking the PLL lock status. + * + */ +static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci) +{ + struct usb_hcd *hcd = xhci_to_hcd(xhci); + u32 pll_lock_check; + u32 retry_count = 4; + + do { + /* Assert PHY reset */ + writel(0x6F, hcd->regs + 0x1048); + udelay(10); + /* De-assert the PHY reset */ + writel(0x7F, hcd->regs + 0x1048); + udelay(200); + pll_lock_check = readl(hcd->regs + 0x1070); + } while (!(pll_lock_check & 0x1) && --retry_count); +} + static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event) { @@ -1556,6 +1585,13 @@ static void handle_port_status(struct xhci_hcd *xhci, goto cleanup; } + /* We might get interrupts after shared_hcd is removed */ + if (port->rhub == &xhci->usb3_rhub && xhci->shared_hcd == NULL) { + xhci_dbg(xhci, "ignore port event for removed USB3 hcd\n"); + bogus_port_status = true; + goto cleanup; + } + hcd = port->rhub->hcd; bus_state = &xhci->bus_state[hcd_index(hcd)]; hcd_portnum = port->hcd_portnum; @@ -1639,7 +1675,7 @@ static void handle_port_status(struct xhci_hcd *xhci, * RExit to a disconnect state). If so, let the the driver know it's * out of the RExit state. */ - if (!DEV_SUPERSPEED_ANY(portsc) && + if (!DEV_SUPERSPEED_ANY(portsc) && hcd->speed < HCD_USB3 && test_and_clear_bit(hcd_portnum, &bus_state->rexit_ports)) { complete(&bus_state->rexit_done[hcd_portnum]); @@ -1647,8 +1683,12 @@ static void handle_port_status(struct xhci_hcd *xhci, goto cleanup; } - if (hcd->speed < HCD_USB3) + if (hcd->speed < HCD_USB3) { xhci_test_and_clear_bit(xhci, port, PORT_PLC); + if ((xhci->quirks & XHCI_RESET_PLL_ON_DISCONNECT) && + (portsc & PORT_CSC) && !(portsc & PORT_CONNECT)) + xhci_cavium_reset_phy_quirk(xhci); + } cleanup: /* Update event ring dequeue pointer before dropping the lock */ @@ -2266,6 +2306,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, goto cleanup; case COMP_RING_UNDERRUN: case COMP_RING_OVERRUN: + case COMP_STOPPED_LENGTH_INVALID: goto cleanup; default: xhci_err(xhci, "ERROR Transfer event for unknown stream ring slot %u ep %u\n", diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 6b5db344de30..938ff06c0349 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1303,6 +1303,7 @@ static int tegra_xusb_remove(struct platform_device *pdev) usb_remove_hcd(xhci->shared_hcd); usb_put_hcd(xhci->shared_hcd); + xhci->shared_hcd = NULL; usb_remove_hcd(tegra->hcd); usb_put_hcd(tegra->hcd); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 0420eefa647a..c928dbbff881 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -719,8 +719,6 @@ static void xhci_stop(struct usb_hcd *hcd) /* Only halt host and free memory after both hcds are removed */ if (!usb_hcd_is_primary_hcd(hcd)) { - /* usb core will free this hcd shortly, unset pointer */ - xhci->shared_hcd = NULL; mutex_unlock(&xhci->mutex); return; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index bf0b3692dc9a..260b259b72bc 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1680,7 +1680,7 @@ struct xhci_bus_state { * It can take up to 20 ms to transition from RExit to U0 on the * Intel Lynx Point LP xHCI host. */ -#define XHCI_MAX_REXIT_TIMEOUT (20 * 1000) +#define XHCI_MAX_REXIT_TIMEOUT_MS 20 static inline unsigned int hcd_index(struct usb_hcd *hcd) { @@ -1849,6 +1849,7 @@ struct xhci_hcd { #define XHCI_INTEL_USB_ROLE_SW BIT_ULL(31) #define XHCI_ZERO_64B_REGS BIT_ULL(32) #define XHCI_DEFAULT_PM_RUNTIME_ALLOW BIT_ULL(33) +#define XHCI_RESET_PLL_ON_DISCONNECT BIT_ULL(34) unsigned int num_active_eps; unsigned int limit_active_eps; diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c index bd539f3058bc..85b48c6ddc7e 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -50,6 +50,7 @@ static const struct usb_device_id appledisplay_table[] = { { APPLEDISPLAY_DEVICE(0x9219) }, { APPLEDISPLAY_DEVICE(0x921c) }, { APPLEDISPLAY_DEVICE(0x921d) }, + { APPLEDISPLAY_DEVICE(0x9222) }, { APPLEDISPLAY_DEVICE(0x9236) }, /* Terminating entry */ |