diff options
Diffstat (limited to 'drivers/gpu')
230 files changed, 10597 insertions, 9890 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index 84d68d658f8a..32809f749903 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -30,25 +30,38 @@ const struct kfd2kgd_calls *kfd2kgd; const struct kgd2kfd_calls *kgd2kfd; bool (*kgd2kfd_init_p)(unsigned, const struct kgd2kfd_calls**); -bool amdgpu_amdkfd_init(void) +int amdgpu_amdkfd_init(void) { + int ret; + #if defined(CONFIG_HSA_AMD_MODULE) - bool (*kgd2kfd_init_p)(unsigned, const struct kgd2kfd_calls**); + int (*kgd2kfd_init_p)(unsigned, const struct kgd2kfd_calls**); kgd2kfd_init_p = symbol_request(kgd2kfd_init); if (kgd2kfd_init_p == NULL) - return false; + return -ENOENT; + + ret = kgd2kfd_init_p(KFD_INTERFACE_VERSION, &kgd2kfd); + if (ret) { + symbol_put(kgd2kfd_init); + kgd2kfd = NULL; + } + +#elif defined(CONFIG_HSA_AMD) + ret = kgd2kfd_init(KFD_INTERFACE_VERSION, &kgd2kfd); + if (ret) + kgd2kfd = NULL; + +#else + ret = -ENOENT; #endif - return true; + + return ret; } bool amdgpu_amdkfd_load_interface(struct amdgpu_device *rdev) { -#if defined(CONFIG_HSA_AMD_MODULE) - bool (*kgd2kfd_init_p)(unsigned, const struct kgd2kfd_calls**); -#endif - switch (rdev->asic_type) { #ifdef CONFIG_DRM_AMDGPU_CIK case CHIP_KAVERI: @@ -62,35 +75,7 @@ bool amdgpu_amdkfd_load_interface(struct amdgpu_device *rdev) return false; } -#if defined(CONFIG_HSA_AMD_MODULE) - kgd2kfd_init_p = symbol_request(kgd2kfd_init); - - if (kgd2kfd_init_p == NULL) { - kfd2kgd = NULL; - return false; - } - - if (!kgd2kfd_init_p(KFD_INTERFACE_VERSION, &kgd2kfd)) { - symbol_put(kgd2kfd_init); - kfd2kgd = NULL; - kgd2kfd = NULL; - - return false; - } - return true; -#elif defined(CONFIG_HSA_AMD) - if (!kgd2kfd_init(KFD_INTERFACE_VERSION, &kgd2kfd)) { - kfd2kgd = NULL; - kgd2kfd = NULL; - return false; - } - - return true; -#else - kfd2kgd = NULL; - return false; -#endif } void amdgpu_amdkfd_fini(void) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h index a8be765542e6..de530f68d4e3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h @@ -36,7 +36,7 @@ struct kgd_mem { void *cpu_ptr; }; -bool amdgpu_amdkfd_init(void); +int amdgpu_amdkfd_init(void); void amdgpu_amdkfd_fini(void); bool amdgpu_amdkfd_load_interface(struct amdgpu_device *rdev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index ce79a8b605a0..01b4fd6115c2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -310,6 +310,14 @@ static int amdgpu_pci_probe(struct pci_dev *pdev, return -ENODEV; } + /* + * Initialize amdkfd before starting radeon. If it was not loaded yet, + * defer radeon probing + */ + ret = amdgpu_amdkfd_init(); + if (ret == -EPROBE_DEFER) + return ret; + /* Get rid of things like offb */ ret = amdgpu_kick_out_firmware_fb(pdev); if (ret) @@ -552,8 +560,6 @@ static int __init amdgpu_init(void) driver->num_ioctls = amdgpu_max_kms_ioctl; amdgpu_register_atpx_handler(); - amdgpu_amdkfd_init(); - /* let modprobe override vga console setting */ return drm_pci_init(driver, pdriver); } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_module.c b/drivers/gpu/drm/amd/amdkfd/kfd_module.c index ca8410e8683d..850a5623661f 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_module.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_module.c @@ -59,18 +59,23 @@ module_param(send_sigterm, int, 0444); MODULE_PARM_DESC(send_sigterm, "Send sigterm to HSA process on unhandled exception (0 = disable, 1 = enable)"); -bool kgd2kfd_init(unsigned interface_version, const struct kgd2kfd_calls **g2f) +static int amdkfd_init_completed; + +int kgd2kfd_init(unsigned interface_version, const struct kgd2kfd_calls **g2f) { + if (!amdkfd_init_completed) + return -EPROBE_DEFER; + /* * Only one interface version is supported, * no kfd/kgd version skew allowed. */ if (interface_version != KFD_INTERFACE_VERSION) - return false; + return -EINVAL; *g2f = &kgd2kfd; - return true; + return 0; } EXPORT_SYMBOL(kgd2kfd_init); @@ -111,6 +116,8 @@ static int __init kfd_module_init(void) kfd_process_create_wq(); + amdkfd_init_completed = 1; + dev_info(kfd_device, "Initialized module\n"); return 0; @@ -125,6 +132,8 @@ err_pasid: static void __exit kfd_module_exit(void) { + amdkfd_init_completed = 0; + kfd_process_destroy_wq(); kfd_topology_shutdown(); kfd_chardev_exit(); diff --git a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h index 888250b33ea8..a09d9f352871 100644 --- a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h @@ -221,7 +221,7 @@ struct kgd2kfd_calls { int (*resume)(struct kfd_dev *kfd); }; -bool kgd2kfd_init(unsigned interface_version, +int kgd2kfd_init(unsigned interface_version, const struct kgd2kfd_calls **g2f); #endif /* KGD_KFD_INTERFACE_H_INCLUDED */ diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index 493c05c9ce4f..bc98bb94264d 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -149,3 +149,37 @@ int drm_of_component_probe(struct device *dev, return component_master_add_with_match(dev, m_ops, match); } EXPORT_SYMBOL(drm_of_component_probe); + +/* + * drm_of_encoder_active_endpoint - return the active encoder endpoint + * @node: device tree node containing encoder input ports + * @encoder: drm_encoder + * + * Given an encoder device node and a drm_encoder with a connected crtc, + * parse the encoder endpoint connecting to the crtc port. + */ +int drm_of_encoder_active_endpoint(struct device_node *node, + struct drm_encoder *encoder, + struct of_endpoint *endpoint) +{ + struct device_node *ep; + struct drm_crtc *crtc = encoder->crtc; + struct device_node *port; + int ret; + + if (!node || !crtc) + return -EINVAL; + + for_each_endpoint_of_node(node, ep) { + port = of_graph_get_remote_port(ep); + of_node_put(port); + if (port == crtc->port) { + ret = of_graph_parse_endpoint(ep, endpoint); + of_node_put(ep); + return ret; + } + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c index 332c55ebba6d..d8d556457427 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_buffer.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_buffer.c @@ -21,6 +21,7 @@ #include "common.xml.h" #include "state.xml.h" +#include "state_3d.xml.h" #include "cmdstream.xml.h" /* @@ -85,10 +86,17 @@ static inline void CMD_STALL(struct etnaviv_cmdbuf *buffer, OUT(buffer, VIV_FE_STALL_TOKEN_FROM(from) | VIV_FE_STALL_TOKEN_TO(to)); } -static void etnaviv_cmd_select_pipe(struct etnaviv_cmdbuf *buffer, u8 pipe) +static inline void CMD_SEM(struct etnaviv_cmdbuf *buffer, u32 from, u32 to) { - u32 flush; - u32 stall; + CMD_LOAD_STATE(buffer, VIVS_GL_SEMAPHORE_TOKEN, + VIVS_GL_SEMAPHORE_TOKEN_FROM(from) | + VIVS_GL_SEMAPHORE_TOKEN_TO(to)); +} + +static void etnaviv_cmd_select_pipe(struct etnaviv_gpu *gpu, + struct etnaviv_cmdbuf *buffer, u8 pipe) +{ + u32 flush = 0; /* * This assumes that if we're switching to 2D, we're switching @@ -96,17 +104,13 @@ static void etnaviv_cmd_select_pipe(struct etnaviv_cmdbuf *buffer, u8 pipe) * the 2D core, we need to flush the 3D depth and color caches, * otherwise we need to flush the 2D pixel engine cache. */ - if (pipe == ETNA_PIPE_2D) - flush = VIVS_GL_FLUSH_CACHE_DEPTH | VIVS_GL_FLUSH_CACHE_COLOR; - else + if (gpu->exec_state == ETNA_PIPE_2D) flush = VIVS_GL_FLUSH_CACHE_PE2D; - - stall = VIVS_GL_SEMAPHORE_TOKEN_FROM(SYNC_RECIPIENT_FE) | - VIVS_GL_SEMAPHORE_TOKEN_TO(SYNC_RECIPIENT_PE); + else if (gpu->exec_state == ETNA_PIPE_3D) + flush = VIVS_GL_FLUSH_CACHE_DEPTH | VIVS_GL_FLUSH_CACHE_COLOR; CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_CACHE, flush); - CMD_LOAD_STATE(buffer, VIVS_GL_SEMAPHORE_TOKEN, stall); - + CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT, @@ -131,6 +135,36 @@ static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu, ptr, len * 4, 0); } +/* + * Safely replace the WAIT of a waitlink with a new command and argument. + * The GPU may be executing this WAIT while we're modifying it, so we have + * to write it in a specific order to avoid the GPU branching to somewhere + * else. 'wl_offset' is the offset to the first byte of the WAIT command. + */ +static void etnaviv_buffer_replace_wait(struct etnaviv_cmdbuf *buffer, + unsigned int wl_offset, u32 cmd, u32 arg) +{ + u32 *lw = buffer->vaddr + wl_offset; + + lw[1] = arg; + mb(); + lw[0] = cmd; + mb(); +} + +/* + * Ensure that there is space in the command buffer to contiguously write + * 'cmd_dwords' 64-bit words into the buffer, wrapping if necessary. + */ +static u32 etnaviv_buffer_reserve(struct etnaviv_gpu *gpu, + struct etnaviv_cmdbuf *buffer, unsigned int cmd_dwords) +{ + if (buffer->user_size + cmd_dwords * sizeof(u64) > buffer->size) + buffer->user_size = 0; + + return gpu_va(gpu, buffer) + buffer->user_size; +} + u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu) { struct etnaviv_cmdbuf *buffer = gpu->buffer; @@ -147,81 +181,79 @@ u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu) void etnaviv_buffer_end(struct etnaviv_gpu *gpu) { struct etnaviv_cmdbuf *buffer = gpu->buffer; + unsigned int waitlink_offset = buffer->user_size - 16; + u32 link_target, flush = 0; - /* Replace the last WAIT with an END */ - buffer->user_size -= 16; - - CMD_END(buffer); - mb(); + if (gpu->exec_state == ETNA_PIPE_2D) + flush = VIVS_GL_FLUSH_CACHE_PE2D; + else if (gpu->exec_state == ETNA_PIPE_3D) + flush = VIVS_GL_FLUSH_CACHE_DEPTH | + VIVS_GL_FLUSH_CACHE_COLOR | + VIVS_GL_FLUSH_CACHE_TEXTURE | + VIVS_GL_FLUSH_CACHE_TEXTUREVS | + VIVS_GL_FLUSH_CACHE_SHADER_L2; + + if (flush) { + unsigned int dwords = 7; + + link_target = etnaviv_buffer_reserve(gpu, buffer, dwords); + + CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); + CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); + CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_CACHE, flush); + if (gpu->exec_state == ETNA_PIPE_3D) + CMD_LOAD_STATE(buffer, VIVS_TS_FLUSH_CACHE, + VIVS_TS_FLUSH_CACHE_FLUSH); + CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); + CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE); + CMD_END(buffer); + + etnaviv_buffer_replace_wait(buffer, waitlink_offset, + VIV_FE_LINK_HEADER_OP_LINK | + VIV_FE_LINK_HEADER_PREFETCH(dwords), + link_target); + } else { + /* Replace the last link-wait with an "END" command */ + etnaviv_buffer_replace_wait(buffer, waitlink_offset, + VIV_FE_END_HEADER_OP_END, 0); + } } +/* Append a command buffer to the ring buffer. */ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, struct etnaviv_cmdbuf *cmdbuf) { struct etnaviv_cmdbuf *buffer = gpu->buffer; - u32 *lw = buffer->vaddr + buffer->user_size - 16; - u32 back, link_target, link_size, reserve_size, extra_size = 0; + unsigned int waitlink_offset = buffer->user_size - 16; + u32 return_target, return_dwords; + u32 link_target, link_dwords; if (drm_debug & DRM_UT_DRIVER) etnaviv_buffer_dump(gpu, buffer, 0, 0x50); + link_target = gpu_va(gpu, cmdbuf); + link_dwords = cmdbuf->size / 8; + /* - * If we need to flush the MMU prior to submitting this buffer, we - * will need to append a mmu flush load state, followed by a new + * If we need maintanence prior to submitting this buffer, we will + * need to append a mmu flush load state, followed by a new * link to this buffer - a total of four additional words. */ if (gpu->mmu->need_flush || gpu->switch_context) { + u32 target, extra_dwords; + /* link command */ - extra_size += 2; + extra_dwords = 1; + /* flush command */ if (gpu->mmu->need_flush) - extra_size += 2; + extra_dwords += 1; + /* pipe switch commands */ if (gpu->switch_context) - extra_size += 8; - } + extra_dwords += 4; - reserve_size = (6 + extra_size) * 4; - - /* - * if we are going to completely overflow the buffer, we need to wrap. - */ - if (buffer->user_size + reserve_size > buffer->size) - buffer->user_size = 0; - - /* save offset back into main buffer */ - back = buffer->user_size + reserve_size - 6 * 4; - link_target = gpu_va(gpu, buffer) + buffer->user_size; - link_size = 6; - - /* Skip over any extra instructions */ - link_target += extra_size * sizeof(u32); - - if (drm_debug & DRM_UT_DRIVER) - pr_info("stream link to 0x%08x @ 0x%08x %p\n", - link_target, gpu_va(gpu, cmdbuf), cmdbuf->vaddr); - - /* jump back from cmd to main buffer */ - CMD_LINK(cmdbuf, link_size, link_target); - - link_target = gpu_va(gpu, cmdbuf); - link_size = cmdbuf->size / 8; - - - - if (drm_debug & DRM_UT_DRIVER) { - print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4, - cmdbuf->vaddr, cmdbuf->size, 0); - - pr_info("link op: %p\n", lw); - pr_info("link addr: %p\n", lw + 1); - pr_info("addr: 0x%08x\n", link_target); - pr_info("back: 0x%08x\n", gpu_va(gpu, buffer) + back); - pr_info("event: %d\n", event); - } - - if (gpu->mmu->need_flush || gpu->switch_context) { - u32 new_target = gpu_va(gpu, buffer) + buffer->user_size; + target = etnaviv_buffer_reserve(gpu, buffer, extra_dwords); if (gpu->mmu->need_flush) { /* Add the MMU flush */ @@ -236,32 +268,59 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event, } if (gpu->switch_context) { - etnaviv_cmd_select_pipe(buffer, cmdbuf->exec_state); + etnaviv_cmd_select_pipe(gpu, buffer, cmdbuf->exec_state); + gpu->exec_state = cmdbuf->exec_state; gpu->switch_context = false; } - /* And the link to the first buffer */ - CMD_LINK(buffer, link_size, link_target); + /* And the link to the submitted buffer */ + CMD_LINK(buffer, link_dwords, link_target); /* Update the link target to point to above instructions */ - link_target = new_target; - link_size = extra_size; + link_target = target; + link_dwords = extra_dwords; } - /* trigger event */ + /* + * Append a LINK to the submitted command buffer to return to + * the ring buffer. return_target is the ring target address. + * We need three dwords: event, wait, link. + */ + return_dwords = 3; + return_target = etnaviv_buffer_reserve(gpu, buffer, return_dwords); + CMD_LINK(cmdbuf, return_dwords, return_target); + + /* + * Append event, wait and link pointing back to the wait + * command to the ring buffer. + */ CMD_LOAD_STATE(buffer, VIVS_GL_EVENT, VIVS_GL_EVENT_EVENT_ID(event) | VIVS_GL_EVENT_FROM_PE); - - /* append WAIT/LINK to main buffer */ CMD_WAIT(buffer); - CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + (buffer->user_size - 4)); + CMD_LINK(buffer, 2, return_target + 8); - /* Change WAIT into a LINK command; write the address first. */ - *(lw + 1) = link_target; - mb(); - *(lw) = VIV_FE_LINK_HEADER_OP_LINK | - VIV_FE_LINK_HEADER_PREFETCH(link_size); - mb(); + if (drm_debug & DRM_UT_DRIVER) + pr_info("stream link to 0x%08x @ 0x%08x %p\n", + return_target, gpu_va(gpu, cmdbuf), cmdbuf->vaddr); + + if (drm_debug & DRM_UT_DRIVER) { + print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4, + cmdbuf->vaddr, cmdbuf->size, 0); + + pr_info("link op: %p\n", buffer->vaddr + waitlink_offset); + pr_info("addr: 0x%08x\n", link_target); + pr_info("back: 0x%08x\n", return_target); + pr_info("event: %d\n", event); + } + + /* + * Kick off the submitted command by replacing the previous + * WAIT with a link to the address in the ring buffer. + */ + etnaviv_buffer_replace_wait(buffer, waitlink_offset, + VIV_FE_LINK_HEADER_OP_LINK | + VIV_FE_LINK_HEADER_PREFETCH(link_dwords), + link_target); if (drm_debug & DRM_UT_DRIVER) etnaviv_buffer_dump(gpu, buffer, 0, 0x50); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.h b/drivers/gpu/drm/etnaviv/etnaviv_drv.h index 1cd6046e76b1..115c5bc6d7c8 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.h @@ -75,9 +75,6 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data, int etnaviv_gem_mmap(struct file *filp, struct vm_area_struct *vma); int etnaviv_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); int etnaviv_gem_mmap_offset(struct drm_gem_object *obj, u64 *offset); -int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, - struct drm_gem_object *obj, u32 *iova); -void etnaviv_gem_put_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj); struct sg_table *etnaviv_gem_prime_get_sg_table(struct drm_gem_object *obj); void *etnaviv_gem_prime_vmap(struct drm_gem_object *obj); void etnaviv_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c index 4b519e4309b2..937a77520f58 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c @@ -260,8 +260,32 @@ etnaviv_gem_get_vram_mapping(struct etnaviv_gem_object *obj, return NULL; } -int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, - struct drm_gem_object *obj, u32 *iova) +void etnaviv_gem_mapping_reference(struct etnaviv_vram_mapping *mapping) +{ + struct etnaviv_gem_object *etnaviv_obj = mapping->object; + + drm_gem_object_reference(&etnaviv_obj->base); + + mutex_lock(&etnaviv_obj->lock); + WARN_ON(mapping->use == 0); + mapping->use += 1; + mutex_unlock(&etnaviv_obj->lock); +} + +void etnaviv_gem_mapping_unreference(struct etnaviv_vram_mapping *mapping) +{ + struct etnaviv_gem_object *etnaviv_obj = mapping->object; + + mutex_lock(&etnaviv_obj->lock); + WARN_ON(mapping->use == 0); + mapping->use -= 1; + mutex_unlock(&etnaviv_obj->lock); + + drm_gem_object_unreference_unlocked(&etnaviv_obj->base); +} + +struct etnaviv_vram_mapping *etnaviv_gem_mapping_get( + struct drm_gem_object *obj, struct etnaviv_gpu *gpu) { struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); struct etnaviv_vram_mapping *mapping; @@ -329,28 +353,12 @@ int etnaviv_gem_get_iova(struct etnaviv_gpu *gpu, out: mutex_unlock(&etnaviv_obj->lock); - if (!ret) { - /* Take a reference on the object */ - drm_gem_object_reference(obj); - *iova = mapping->iova; - } - - return ret; -} - -void etnaviv_gem_put_iova(struct etnaviv_gpu *gpu, struct drm_gem_object *obj) -{ - struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj); - struct etnaviv_vram_mapping *mapping; - - mutex_lock(&etnaviv_obj->lock); - mapping = etnaviv_gem_get_vram_mapping(etnaviv_obj, gpu->mmu); - - WARN_ON(mapping->use == 0); - mapping->use -= 1; - mutex_unlock(&etnaviv_obj->lock); + if (ret) + return ERR_PTR(ret); - drm_gem_object_unreference_unlocked(obj); + /* Take a reference on the object */ + drm_gem_object_reference(obj); + return mapping; } void *etnaviv_gem_vmap(struct drm_gem_object *obj) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.h b/drivers/gpu/drm/etnaviv/etnaviv_gem.h index ab5df8147a5f..02665d8c10ee 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.h @@ -88,6 +88,12 @@ static inline bool is_active(struct etnaviv_gem_object *etnaviv_obj) #define MAX_CMDS 4 +struct etnaviv_gem_submit_bo { + u32 flags; + struct etnaviv_gem_object *obj; + struct etnaviv_vram_mapping *mapping; +}; + /* Created per submit-ioctl, to track bo's and cmdstream bufs, etc, * associated with the cmdstream submission for synchronization (and * make it easier to unwind when things go wrong, etc). This only @@ -99,11 +105,7 @@ struct etnaviv_gem_submit { struct ww_acquire_ctx ticket; u32 fence; unsigned int nr_bos; - struct { - u32 flags; - struct etnaviv_gem_object *obj; - u32 iova; - } bos[0]; + struct etnaviv_gem_submit_bo bos[0]; }; int etnaviv_gem_wait_bo(struct etnaviv_gpu *gpu, struct drm_gem_object *obj, @@ -115,4 +117,9 @@ int etnaviv_gem_obj_add(struct drm_device *dev, struct drm_gem_object *obj); struct page **etnaviv_gem_get_pages(struct etnaviv_gem_object *obj); void etnaviv_gem_put_pages(struct etnaviv_gem_object *obj); +struct etnaviv_vram_mapping *etnaviv_gem_mapping_get( + struct drm_gem_object *obj, struct etnaviv_gpu *gpu); +void etnaviv_gem_mapping_reference(struct etnaviv_vram_mapping *mapping); +void etnaviv_gem_mapping_unreference(struct etnaviv_vram_mapping *mapping); + #endif /* __ETNAVIV_GEM_H__ */ diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index 1aba01a999df..236ada93df53 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -187,12 +187,10 @@ static void submit_unpin_objects(struct etnaviv_gem_submit *submit) int i; for (i = 0; i < submit->nr_bos; i++) { - struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; - if (submit->bos[i].flags & BO_PINNED) - etnaviv_gem_put_iova(submit->gpu, &etnaviv_obj->base); + etnaviv_gem_mapping_unreference(submit->bos[i].mapping); - submit->bos[i].iova = 0; + submit->bos[i].mapping = NULL; submit->bos[i].flags &= ~BO_PINNED; } } @@ -203,22 +201,24 @@ static int submit_pin_objects(struct etnaviv_gem_submit *submit) for (i = 0; i < submit->nr_bos; i++) { struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; - u32 iova; + struct etnaviv_vram_mapping *mapping; - ret = etnaviv_gem_get_iova(submit->gpu, &etnaviv_obj->base, - &iova); - if (ret) + mapping = etnaviv_gem_mapping_get(&etnaviv_obj->base, + submit->gpu); + if (IS_ERR(mapping)) { + ret = PTR_ERR(mapping); break; + } submit->bos[i].flags |= BO_PINNED; - submit->bos[i].iova = iova; + submit->bos[i].mapping = mapping; } return ret; } static int submit_bo(struct etnaviv_gem_submit *submit, u32 idx, - struct etnaviv_gem_object **obj, u32 *iova) + struct etnaviv_gem_submit_bo **bo) { if (idx >= submit->nr_bos) { DRM_ERROR("invalid buffer index: %u (out of %u)\n", @@ -226,10 +226,7 @@ static int submit_bo(struct etnaviv_gem_submit *submit, u32 idx, return -EINVAL; } - if (obj) - *obj = submit->bos[idx].obj; - if (iova) - *iova = submit->bos[idx].iova; + *bo = &submit->bos[idx]; return 0; } @@ -245,8 +242,8 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, void *stream, for (i = 0; i < nr_relocs; i++) { const struct drm_etnaviv_gem_submit_reloc *r = relocs + i; - struct etnaviv_gem_object *bobj; - u32 iova, off; + struct etnaviv_gem_submit_bo *bo; + u32 off; if (unlikely(r->flags)) { DRM_ERROR("invalid reloc flags\n"); @@ -268,17 +265,16 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, void *stream, return -EINVAL; } - ret = submit_bo(submit, r->reloc_idx, &bobj, &iova); + ret = submit_bo(submit, r->reloc_idx, &bo); if (ret) return ret; - if (r->reloc_offset >= - bobj->base.size - sizeof(*ptr)) { + if (r->reloc_offset >= bo->obj->base.size - sizeof(*ptr)) { DRM_ERROR("relocation %u outside object", i); return -EINVAL; } - ptr[off] = iova + r->reloc_offset; + ptr[off] = bo->mapping->iova + r->reloc_offset; last_offset = off; } diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index a33162cf4f4c..d13303ce530d 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -628,6 +628,7 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu) /* Now program the hardware */ mutex_lock(&gpu->lock); etnaviv_gpu_hw_init(gpu); + gpu->exec_state = -1; mutex_unlock(&gpu->lock); pm_runtime_mark_last_busy(gpu->dev); @@ -871,17 +872,13 @@ static void recover_worker(struct work_struct *work) gpu->event[i].fence = NULL; gpu->event[i].used = false; complete(&gpu->event_free); - /* - * Decrement the PM count for each stuck event. This is safe - * even in atomic context as we use ASYNC RPM here. - */ - pm_runtime_put_autosuspend(gpu->dev); } spin_unlock_irqrestore(&gpu->event_spinlock, flags); gpu->completed_fence = gpu->active_fence; etnaviv_gpu_hw_init(gpu); gpu->switch_context = true; + gpu->exec_state = -1; mutex_unlock(&gpu->lock); pm_runtime_mark_last_busy(gpu->dev); @@ -1106,7 +1103,7 @@ struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, u32 size, size_t nr_bos) { struct etnaviv_cmdbuf *cmdbuf; - size_t sz = size_vstruct(nr_bos, sizeof(cmdbuf->bo[0]), + size_t sz = size_vstruct(nr_bos, sizeof(cmdbuf->bo_map[0]), sizeof(*cmdbuf)); cmdbuf = kzalloc(sz, GFP_KERNEL); @@ -1150,14 +1147,23 @@ static void retire_worker(struct work_struct *work) fence_put(cmdbuf->fence); for (i = 0; i < cmdbuf->nr_bos; i++) { - struct etnaviv_gem_object *etnaviv_obj = cmdbuf->bo[i]; + struct etnaviv_vram_mapping *mapping = cmdbuf->bo_map[i]; + struct etnaviv_gem_object *etnaviv_obj = mapping->object; atomic_dec(&etnaviv_obj->gpu_active); /* drop the refcount taken in etnaviv_gpu_submit */ - etnaviv_gem_put_iova(gpu, &etnaviv_obj->base); + etnaviv_gem_mapping_unreference(mapping); } etnaviv_gpu_cmdbuf_free(cmdbuf); + /* + * We need to balance the runtime PM count caused by + * each submission. Upon submission, we increment + * the runtime PM counter, and allocate one event. + * So here, we put the runtime PM count for each + * completed event. + */ + pm_runtime_put_autosuspend(gpu->dev); } gpu->retired_fence = fence; @@ -1304,11 +1310,10 @@ int etnaviv_gpu_submit(struct etnaviv_gpu *gpu, for (i = 0; i < submit->nr_bos; i++) { struct etnaviv_gem_object *etnaviv_obj = submit->bos[i].obj; - u32 iova; - /* Each cmdbuf takes a refcount on the iova */ - etnaviv_gem_get_iova(gpu, &etnaviv_obj->base, &iova); - cmdbuf->bo[i] = etnaviv_obj; + /* Each cmdbuf takes a refcount on the mapping */ + etnaviv_gem_mapping_reference(submit->bos[i].mapping); + cmdbuf->bo_map[i] = submit->bos[i].mapping; atomic_inc(&etnaviv_obj->gpu_active); if (submit->bos[i].flags & ETNA_SUBMIT_BO_WRITE) @@ -1378,15 +1383,6 @@ static irqreturn_t irq_handler(int irq, void *data) gpu->completed_fence = fence->seqno; event_free(gpu, event); - - /* - * We need to balance the runtime PM count caused by - * each submission. Upon submission, we increment - * the runtime PM counter, and allocate one event. - * So here, we put the runtime PM count for each - * completed event. - */ - pm_runtime_put_autosuspend(gpu->dev); } /* Retire the buffer objects in a work */ @@ -1481,6 +1477,7 @@ static int etnaviv_gpu_hw_resume(struct etnaviv_gpu *gpu) etnaviv_gpu_hw_init(gpu); gpu->switch_context = true; + gpu->exec_state = -1; mutex_unlock(&gpu->lock); @@ -1569,6 +1566,7 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct etnaviv_gpu *gpu; + u32 dma_mask; int err = 0; gpu = devm_kzalloc(dev, sizeof(*gpu), GFP_KERNEL); @@ -1579,12 +1577,16 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev) mutex_init(&gpu->lock); /* - * Set the GPU base address to the start of physical memory. This - * ensures that if we have up to 2GB, the v1 MMU can address the - * highest memory. This is important as command buffers may be - * allocated outside of this limit. + * Set the GPU linear window to be at the end of the DMA window, where + * the CMA area is likely to reside. This ensures that we are able to + * map the command buffers while having the linear window overlap as + * much RAM as possible, so we can optimize mappings for other buffers. */ - gpu->memory_base = PHYS_OFFSET; + dma_mask = (u32)dma_get_required_mask(dev); + if (dma_mask < PHYS_OFFSET + SZ_2G) + gpu->memory_base = PHYS_OFFSET; + else + gpu->memory_base = dma_mask - SZ_2G + 1; /* Map registers: */ gpu->mmio = etnaviv_ioremap(pdev, NULL, dev_name(gpu->dev)); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h index f233ac4c7c1c..f5321e2f25ff 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.h +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.h @@ -23,6 +23,7 @@ #include "etnaviv_drv.h" struct etnaviv_gem_submit; +struct etnaviv_vram_mapping; struct etnaviv_chip_identity { /* Chip model. */ @@ -103,6 +104,7 @@ struct etnaviv_gpu { /* 'ring'-buffer: */ struct etnaviv_cmdbuf *buffer; + int exec_state; /* bus base address of memory */ u32 memory_base; @@ -166,7 +168,7 @@ struct etnaviv_cmdbuf { struct list_head node; /* BOs attached to this command buffer */ unsigned int nr_bos; - struct etnaviv_gem_object *bo[0]; + struct etnaviv_vram_mapping *bo_map[0]; }; static inline void gpu_write(struct etnaviv_gpu *gpu, u32 reg, u32 data) diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c index 6743bc648dc8..29a723fabc17 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c @@ -193,7 +193,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu, /* * Unmap the blocks which need to be reaped from the MMU. - * Clear the mmu pointer to prevent the get_iova finding + * Clear the mmu pointer to prevent the mapping_get finding * this mapping. */ list_for_each_entry_safe(m, n, &list, scan_node) { diff --git a/drivers/gpu/drm/etnaviv/state_3d.xml.h b/drivers/gpu/drm/etnaviv/state_3d.xml.h new file mode 100644 index 000000000000..d7146fd13943 --- /dev/null +++ b/drivers/gpu/drm/etnaviv/state_3d.xml.h @@ -0,0 +1,9 @@ +#ifndef STATE_3D_XML +#define STATE_3D_XML + +/* This is a cut-down version of the state_3d.xml.h file */ + +#define VIVS_TS_FLUSH_CACHE 0x00001650 +#define VIVS_TS_FLUSH_CACHE_FLUSH 0x00000001 + +#endif /* STATE_3D_XML */ diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 83efca941388..f17d39279596 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -1,6 +1,6 @@ config DRM_EXYNOS tristate "DRM Support for Samsung SoC EXYNOS Series" - depends on OF && DRM && (PLAT_SAMSUNG || ARCH_MULTIPLATFORM) + depends on OF && DRM && (ARCH_S3C64XX || ARCH_EXYNOS || ARCH_MULTIPLATFORM) select DRM_KMS_HELPER select DRM_KMS_FB_HELPER select FB_CFB_FILLRECT diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 6496532aaa91..968b31c522b2 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -2,7 +2,6 @@ # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos exynosdrm-y := exynos_drm_drv.o exynos_drm_crtc.o exynos_drm_fbdev.o \ exynos_drm_fb.o exynos_drm_gem.o exynos_drm_core.o \ exynos_drm_plane.o diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c index 1bf6a21130c7..5245bc5e82e9 100644 --- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c @@ -93,7 +93,7 @@ static int decon_enable_vblank(struct exynos_drm_crtc *crtc) if (test_bit(BIT_SUSPENDED, &ctx->flags)) return -EPERM; - if (test_and_set_bit(BIT_IRQS_ENABLED, &ctx->flags)) { + if (!test_and_set_bit(BIT_IRQS_ENABLED, &ctx->flags)) { val = VIDINTCON0_INTEN; if (ctx->out_type == IFTYPE_I80) val |= VIDINTCON0_FRAMEDONE; @@ -402,8 +402,6 @@ static void decon_enable(struct exynos_drm_crtc *crtc) decon_enable_vblank(ctx->crtc); decon_commit(ctx->crtc); - - set_bit(BIT_SUSPENDED, &ctx->flags); } static void decon_disable(struct exynos_drm_crtc *crtc) @@ -431,7 +429,7 @@ static void decon_disable(struct exynos_drm_crtc *crtc) set_bit(BIT_SUSPENDED, &ctx->flags); } -void decon_te_irq_handler(struct exynos_drm_crtc *crtc) +static void decon_te_irq_handler(struct exynos_drm_crtc *crtc) { struct decon_context *ctx = crtc->ctx; @@ -582,9 +580,9 @@ out: static int exynos5433_decon_suspend(struct device *dev) { struct decon_context *ctx = dev_get_drvdata(dev); - int i; + int i = ARRAY_SIZE(decon_clks_name); - for (i = 0; i < ARRAY_SIZE(decon_clks_name); i++) + while (--i >= 0) clk_disable_unprepare(ctx->clks[i]); return 0; diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c index 52bda3b42fe0..93361073af9a 100644 --- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c @@ -60,7 +60,6 @@ struct decon_context { wait_queue_head_t wait_vsync_queue; atomic_t wait_vsync_event; - struct exynos_drm_panel_info panel; struct drm_encoder *encoder; }; diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c index 9fd12c621270..cff8dc788820 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.c +++ b/drivers/gpu/drm/exynos/exynos_dp_core.c @@ -977,9 +977,7 @@ static int exynos_dp_get_modes(struct drm_connector *connector) return 0; } - drm_display_mode_from_videomode(&dp->priv.vm, mode); - mode->width_mm = dp->priv.width_mm; - mode->height_mm = dp->priv.height_mm; + drm_display_mode_from_videomode(&dp->vm, mode); connector->display_info.width_mm = mode->width_mm; connector->display_info.height_mm = mode->height_mm; @@ -1241,8 +1239,7 @@ static int exynos_dp_dt_parse_panel(struct exynos_dp_device *dp) { int ret; - ret = of_get_videomode(dp->dev->of_node, &dp->priv.vm, - OF_USE_NATIVE_MODE); + ret = of_get_videomode(dp->dev->of_node, &dp->vm, OF_USE_NATIVE_MODE); if (ret) { DRM_ERROR("failed: of_get_videomode() : %d\n", ret); return ret; diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h index 66eec4b2d5c6..b5c2d8f47f9c 100644 --- a/drivers/gpu/drm/exynos/exynos_dp_core.h +++ b/drivers/gpu/drm/exynos/exynos_dp_core.h @@ -16,6 +16,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_dp_helper.h> #include <drm/exynos_drm.h> +#include <video/videomode.h> #include "exynos_drm_drv.h" @@ -164,8 +165,7 @@ struct exynos_dp_device { struct phy *phy; int dpms_mode; int hpd_gpio; - - struct exynos_drm_panel_info priv; + struct videomode vm; }; /* exynos_dp_reg.c */ diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 1e535f981240..0dcfa04830ea 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -130,6 +130,8 @@ static void exynos_drm_atomic_work(struct work_struct *work) exynos_atomic_commit_complete(commit); } +static struct device *exynos_drm_get_dma_device(void); + static int exynos_drm_load(struct drm_device *dev, unsigned long flags) { struct exynos_drm_private *private; @@ -147,6 +149,16 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) dev_set_drvdata(dev->dev, dev); dev->dev_private = (void *)private; + /* the first real CRTC device is used for all dma mapping operations */ + private->dma_dev = exynos_drm_get_dma_device(); + if (!private->dma_dev) { + DRM_ERROR("no device found for DMA mapping operations.\n"); + ret = -ENODEV; + goto err_free_private; + } + DRM_INFO("Exynos DRM: using %s device for DMA mapping operations\n", + dev_name(private->dma_dev)); + /* * create mapping to manage iommu table and set a pointer to iommu * mapping structure to iommu_mapping of private data. @@ -481,69 +493,65 @@ static const struct dev_pm_ops exynos_drm_pm_ops = { /* forward declaration */ static struct platform_driver exynos_drm_platform_driver; +struct exynos_drm_driver_info { + struct platform_driver *driver; + unsigned int flags; +}; + +#define DRM_COMPONENT_DRIVER BIT(0) /* supports component framework */ +#define DRM_VIRTUAL_DEVICE BIT(1) /* create virtual platform device */ +#define DRM_DMA_DEVICE BIT(2) /* can be used for dma allocations */ + +#define DRV_PTR(drv, cond) (IS_ENABLED(cond) ? &drv : NULL) + /* * Connector drivers should not be placed before associated crtc drivers, * because connector requires pipe number of its crtc during initialization. */ -static struct platform_driver *const exynos_drm_kms_drivers[] = { -#ifdef CONFIG_DRM_EXYNOS_FIMD - &fimd_driver, -#endif -#ifdef CONFIG_DRM_EXYNOS5433_DECON - &exynos5433_decon_driver, -#endif -#ifdef CONFIG_DRM_EXYNOS7_DECON - &decon_driver, -#endif -#ifdef CONFIG_DRM_EXYNOS_MIC - &mic_driver, -#endif -#ifdef CONFIG_DRM_EXYNOS_DP - &dp_driver, -#endif -#ifdef CONFIG_DRM_EXYNOS_DSI - &dsi_driver, -#endif -#ifdef CONFIG_DRM_EXYNOS_MIXER - &mixer_driver, -#endif -#ifdef CONFIG_DRM_EXYNOS_HDMI - &hdmi_driver, -#endif -#ifdef CONFIG_DRM_EXYNOS_VIDI - &vidi_driver, -#endif -}; - -static struct platform_driver *const exynos_drm_non_kms_drivers[] = { -#ifdef CONFIG_DRM_EXYNOS_G2D - &g2d_driver, -#endif -#ifdef CONFIG_DRM_EXYNOS_FIMC - &fimc_driver, -#endif -#ifdef CONFIG_DRM_EXYNOS_ROTATOR - &rotator_driver, -#endif -#ifdef CONFIG_DRM_EXYNOS_GSC - &gsc_driver, -#endif -#ifdef CONFIG_DRM_EXYNOS_IPP - &ipp_driver, -#endif - &exynos_drm_platform_driver, -}; - -static struct platform_driver *const exynos_drm_drv_with_simple_dev[] = { -#ifdef CONFIG_DRM_EXYNOS_VIDI - &vidi_driver, -#endif -#ifdef CONFIG_DRM_EXYNOS_IPP - &ipp_driver, -#endif - &exynos_drm_platform_driver, +static struct exynos_drm_driver_info exynos_drm_drivers[] = { + { + DRV_PTR(fimd_driver, CONFIG_DRM_EXYNOS_FIMD), + DRM_COMPONENT_DRIVER | DRM_DMA_DEVICE + }, { + DRV_PTR(exynos5433_decon_driver, CONFIG_DRM_EXYNOS5433_DECON), + DRM_COMPONENT_DRIVER | DRM_DMA_DEVICE + }, { + DRV_PTR(decon_driver, CONFIG_DRM_EXYNOS7_DECON), + DRM_COMPONENT_DRIVER | DRM_DMA_DEVICE + }, { + DRV_PTR(mixer_driver, CONFIG_DRM_EXYNOS_MIXER), + DRM_COMPONENT_DRIVER | DRM_DMA_DEVICE + }, { + DRV_PTR(mic_driver, CONFIG_DRM_EXYNOS_MIC), + DRM_COMPONENT_DRIVER + }, { + DRV_PTR(dp_driver, CONFIG_DRM_EXYNOS_DP), + DRM_COMPONENT_DRIVER + }, { + DRV_PTR(dsi_driver, CONFIG_DRM_EXYNOS_DSI), + DRM_COMPONENT_DRIVER + }, { + DRV_PTR(hdmi_driver, CONFIG_DRM_EXYNOS_HDMI), + DRM_COMPONENT_DRIVER + }, { + DRV_PTR(vidi_driver, CONFIG_DRM_EXYNOS_VIDI), + DRM_COMPONENT_DRIVER | DRM_VIRTUAL_DEVICE + }, { + DRV_PTR(g2d_driver, CONFIG_DRM_EXYNOS_G2D), + }, { + DRV_PTR(fimc_driver, CONFIG_DRM_EXYNOS_FIMC), + }, { + DRV_PTR(rotator_driver, CONFIG_DRM_EXYNOS_ROTATOR), + }, { + DRV_PTR(gsc_driver, CONFIG_DRM_EXYNOS_GSC), + }, { + DRV_PTR(ipp_driver, CONFIG_DRM_EXYNOS_IPP), + DRM_VIRTUAL_DEVICE + }, { + &exynos_drm_platform_driver, + DRM_VIRTUAL_DEVICE + } }; -#define PDEV_COUNT ARRAY_SIZE(exynos_drm_drv_with_simple_dev) static int compare_dev(struct device *dev, void *data) { @@ -555,11 +563,15 @@ static struct component_match *exynos_drm_match_add(struct device *dev) struct component_match *match = NULL; int i; - for (i = 0; i < ARRAY_SIZE(exynos_drm_kms_drivers); ++i) { - struct device_driver *drv = &exynos_drm_kms_drivers[i]->driver; + for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) { + struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; struct device *p = NULL, *d; - while ((d = bus_find_device(&platform_bus_type, p, drv, + if (!info->driver || !(info->flags & DRM_COMPONENT_DRIVER)) + continue; + + while ((d = bus_find_device(&platform_bus_type, p, + &info->driver->driver, (void *)platform_bus_type.match))) { put_device(p); component_match_add(dev, &match, compare_dev, d); @@ -616,91 +628,102 @@ static struct platform_driver exynos_drm_platform_driver = { }, }; -static struct platform_device *exynos_drm_pdevs[PDEV_COUNT]; +static struct device *exynos_drm_get_dma_device(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) { + struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; + struct device *dev; + + if (!info->driver || !(info->flags & DRM_DMA_DEVICE)) + continue; + + while ((dev = bus_find_device(&platform_bus_type, NULL, + &info->driver->driver, + (void *)platform_bus_type.match))) { + put_device(dev); + return dev; + } + } + return NULL; +} static void exynos_drm_unregister_devices(void) { - int i = PDEV_COUNT; + int i; - while (--i >= 0) { - platform_device_unregister(exynos_drm_pdevs[i]); - exynos_drm_pdevs[i] = NULL; + for (i = ARRAY_SIZE(exynos_drm_drivers) - 1; i >= 0; --i) { + struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; + struct device *dev; + + if (!info->driver || !(info->flags & DRM_VIRTUAL_DEVICE)) + continue; + + while ((dev = bus_find_device(&platform_bus_type, NULL, + &info->driver->driver, + (void *)platform_bus_type.match))) { + put_device(dev); + platform_device_unregister(to_platform_device(dev)); + } } } static int exynos_drm_register_devices(void) { + struct platform_device *pdev; int i; - for (i = 0; i < PDEV_COUNT; ++i) { - struct platform_driver *d = exynos_drm_drv_with_simple_dev[i]; - struct platform_device *pdev = - platform_device_register_simple(d->driver.name, -1, - NULL, 0); + for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) { + struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; - if (!IS_ERR(pdev)) { - exynos_drm_pdevs[i] = pdev; + if (!info->driver || !(info->flags & DRM_VIRTUAL_DEVICE)) continue; - } - while (--i >= 0) { - platform_device_unregister(exynos_drm_pdevs[i]); - exynos_drm_pdevs[i] = NULL; - } - return PTR_ERR(pdev); + pdev = platform_device_register_simple( + info->driver->driver.name, -1, NULL, 0); + if (IS_ERR(pdev)) + goto fail; } return 0; +fail: + exynos_drm_unregister_devices(); + return PTR_ERR(pdev); } -static void exynos_drm_unregister_drivers(struct platform_driver * const *drv, - int count) +static void exynos_drm_unregister_drivers(void) { - while (--count >= 0) - platform_driver_unregister(drv[count]); -} + int i; -static int exynos_drm_register_drivers(struct platform_driver * const *drv, - int count) -{ - int i, ret; + for (i = ARRAY_SIZE(exynos_drm_drivers) - 1; i >= 0; --i) { + struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; - for (i = 0; i < count; ++i) { - ret = platform_driver_register(drv[i]); - if (!ret) + if (!info->driver) continue; - while (--i >= 0) - platform_driver_unregister(drv[i]); - - return ret; + platform_driver_unregister(info->driver); } - - return 0; } -static inline int exynos_drm_register_kms_drivers(void) +static int exynos_drm_register_drivers(void) { - return exynos_drm_register_drivers(exynos_drm_kms_drivers, - ARRAY_SIZE(exynos_drm_kms_drivers)); -} + int i, ret; -static inline int exynos_drm_register_non_kms_drivers(void) -{ - return exynos_drm_register_drivers(exynos_drm_non_kms_drivers, - ARRAY_SIZE(exynos_drm_non_kms_drivers)); -} + for (i = 0; i < ARRAY_SIZE(exynos_drm_drivers); ++i) { + struct exynos_drm_driver_info *info = &exynos_drm_drivers[i]; -static inline void exynos_drm_unregister_kms_drivers(void) -{ - exynos_drm_unregister_drivers(exynos_drm_kms_drivers, - ARRAY_SIZE(exynos_drm_kms_drivers)); -} + if (!info->driver) + continue; -static inline void exynos_drm_unregister_non_kms_drivers(void) -{ - exynos_drm_unregister_drivers(exynos_drm_non_kms_drivers, - ARRAY_SIZE(exynos_drm_non_kms_drivers)); + ret = platform_driver_register(info->driver); + if (ret) + goto fail; + } + return 0; +fail: + exynos_drm_unregister_drivers(); + return ret; } static int exynos_drm_init(void) @@ -711,19 +734,12 @@ static int exynos_drm_init(void) if (ret) return ret; - ret = exynos_drm_register_kms_drivers(); + ret = exynos_drm_register_drivers(); if (ret) goto err_unregister_pdevs; - ret = exynos_drm_register_non_kms_drivers(); - if (ret) - goto err_unregister_kms_drivers; - return 0; -err_unregister_kms_drivers: - exynos_drm_unregister_kms_drivers(); - err_unregister_pdevs: exynos_drm_unregister_devices(); @@ -732,8 +748,7 @@ err_unregister_pdevs: static void exynos_drm_exit(void) { - exynos_drm_unregister_non_kms_drivers(); - exynos_drm_unregister_kms_drivers(); + exynos_drm_unregister_drivers(); exynos_drm_unregister_devices(); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h index 17b5ded72ff1..502f750bad2a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.h +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -219,8 +219,10 @@ struct exynos_drm_private { struct drm_crtc *crtc[MAX_CRTC]; struct drm_property *plane_zpos_property; + struct device *dma_dev; unsigned long da_start; unsigned long da_space_size; + void *mapping; unsigned int pipe; @@ -230,6 +232,13 @@ struct exynos_drm_private { wait_queue_head_t wait; }; +static inline struct device *to_dma_dev(struct drm_device *dev) +{ + struct exynos_drm_private *priv = dev->dev_private; + + return priv->dma_dev; +} + /* * Exynos drm sub driver structure. * @@ -297,7 +306,6 @@ extern struct platform_driver dp_driver; extern struct platform_driver dsi_driver; extern struct platform_driver mixer_driver; extern struct platform_driver hdmi_driver; -extern struct platform_driver exynos_drm_common_hdmi_driver; extern struct platform_driver vidi_driver; extern struct platform_driver g2d_driver; extern struct platform_driver fimc_driver; diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 736115c580fc..63c84a106c0b 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -10,6 +10,8 @@ * published by the Free Software Foundation. */ +#include <asm/unaligned.h> + #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_mipi_dsi.h> @@ -209,12 +211,6 @@ #define OLD_SCLK_MIPI_CLK_NAME "pll_clk" -#define REG_ADDR(dsi, reg_idx) ((dsi)->reg_base + \ - dsi->driver_data->reg_ofs[(reg_idx)]) -#define DSI_WRITE(dsi, reg_idx, val) writel((val), \ - REG_ADDR((dsi), (reg_idx))) -#define DSI_READ(dsi, reg_idx) readl(REG_ADDR((dsi), (reg_idx))) - static char *clk_names[5] = { "bus_clk", "sclk_mipi", "phyclk_mipidphy0_bitclkdiv8", "phyclk_mipidphy0_rxclkesc0", "sclk_rgb_vclk_to_dsim0" }; @@ -228,12 +224,8 @@ struct exynos_dsi_transfer { struct list_head list; struct completion completed; int result; - u8 data_id; - u8 data[2]; + struct mipi_dsi_packet packet; u16 flags; - - const u8 *tx_payload; - u16 tx_len; u16 tx_done; u8 *rx_payload; @@ -247,7 +239,7 @@ struct exynos_dsi_transfer { #define DSIM_STATE_VIDOUT_AVAILABLE BIT(3) struct exynos_dsi_driver_data { - unsigned int *reg_ofs; + const unsigned int *reg_ofs; unsigned int plltmr_reg; unsigned int has_freqband:1; unsigned int has_clklane_stop:1; @@ -255,7 +247,7 @@ struct exynos_dsi_driver_data { unsigned int max_freq; unsigned int wait_for_reset; unsigned int num_bits_resol; - unsigned int *reg_values; + const unsigned int *reg_values; }; struct exynos_dsi { @@ -324,7 +316,20 @@ enum reg_idx { DSIM_PHYTIMING2_REG, NUM_REGS }; -static unsigned int exynos_reg_ofs[] = { + +static inline void exynos_dsi_write(struct exynos_dsi *dsi, enum reg_idx idx, + u32 val) +{ + + writel(val, dsi->reg_base + dsi->driver_data->reg_ofs[idx]); +} + +static inline u32 exynos_dsi_read(struct exynos_dsi *dsi, enum reg_idx idx) +{ + return readl(dsi->reg_base + dsi->driver_data->reg_ofs[idx]); +} + +static const unsigned int exynos_reg_ofs[] = { [DSIM_STATUS_REG] = 0x00, [DSIM_SWRST_REG] = 0x04, [DSIM_CLKCTRL_REG] = 0x08, @@ -348,7 +353,7 @@ static unsigned int exynos_reg_ofs[] = { [DSIM_PHYTIMING2_REG] = 0x6c, }; -static unsigned int exynos5433_reg_ofs[] = { +static const unsigned int exynos5433_reg_ofs[] = { [DSIM_STATUS_REG] = 0x04, [DSIM_SWRST_REG] = 0x0C, [DSIM_CLKCTRL_REG] = 0x10, @@ -390,7 +395,7 @@ enum reg_value_idx { PHYTIMING_HS_TRAIL }; -static unsigned int reg_values[] = { +static const unsigned int reg_values[] = { [RESET_TYPE] = DSIM_SWRST, [PLL_TIMER] = 500, [STOP_STATE_CNT] = 0xf, @@ -408,7 +413,25 @@ static unsigned int reg_values[] = { [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0b), }; -static unsigned int exynos5433_reg_values[] = { +static const unsigned int exynos5422_reg_values[] = { + [RESET_TYPE] = DSIM_SWRST, + [PLL_TIMER] = 500, + [STOP_STATE_CNT] = 0xf, + [PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0xaf), + [PHYCTRL_VREG_LP] = 0, + [PHYCTRL_SLEW_UP] = 0, + [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x08), + [PHYTIMING_HS_EXIT] = DSIM_PHYTIMING_HS_EXIT(0x0d), + [PHYTIMING_CLK_PREPARE] = DSIM_PHYTIMING1_CLK_PREPARE(0x09), + [PHYTIMING_CLK_ZERO] = DSIM_PHYTIMING1_CLK_ZERO(0x30), + [PHYTIMING_CLK_POST] = DSIM_PHYTIMING1_CLK_POST(0x0e), + [PHYTIMING_CLK_TRAIL] = DSIM_PHYTIMING1_CLK_TRAIL(0x0a), + [PHYTIMING_HS_PREPARE] = DSIM_PHYTIMING2_HS_PREPARE(0x0c), + [PHYTIMING_HS_ZERO] = DSIM_PHYTIMING2_HS_ZERO(0x11), + [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0d), +}; + +static const unsigned int exynos5433_reg_values[] = { [RESET_TYPE] = DSIM_FUNCRST, [PLL_TIMER] = 22200, [STOP_STATE_CNT] = 0xa, @@ -426,7 +449,7 @@ static unsigned int exynos5433_reg_values[] = { [PHYTIMING_HS_TRAIL] = DSIM_PHYTIMING2_HS_TRAIL(0x0c), }; -static struct exynos_dsi_driver_data exynos3_dsi_driver_data = { +static const struct exynos_dsi_driver_data exynos3_dsi_driver_data = { .reg_ofs = exynos_reg_ofs, .plltmr_reg = 0x50, .has_freqband = 1, @@ -438,7 +461,7 @@ static struct exynos_dsi_driver_data exynos3_dsi_driver_data = { .reg_values = reg_values, }; -static struct exynos_dsi_driver_data exynos4_dsi_driver_data = { +static const struct exynos_dsi_driver_data exynos4_dsi_driver_data = { .reg_ofs = exynos_reg_ofs, .plltmr_reg = 0x50, .has_freqband = 1, @@ -450,7 +473,7 @@ static struct exynos_dsi_driver_data exynos4_dsi_driver_data = { .reg_values = reg_values, }; -static struct exynos_dsi_driver_data exynos4415_dsi_driver_data = { +static const struct exynos_dsi_driver_data exynos4415_dsi_driver_data = { .reg_ofs = exynos_reg_ofs, .plltmr_reg = 0x58, .has_clklane_stop = 1, @@ -461,7 +484,7 @@ static struct exynos_dsi_driver_data exynos4415_dsi_driver_data = { .reg_values = reg_values, }; -static struct exynos_dsi_driver_data exynos5_dsi_driver_data = { +static const struct exynos_dsi_driver_data exynos5_dsi_driver_data = { .reg_ofs = exynos_reg_ofs, .plltmr_reg = 0x58, .num_clks = 2, @@ -471,7 +494,7 @@ static struct exynos_dsi_driver_data exynos5_dsi_driver_data = { .reg_values = reg_values, }; -static struct exynos_dsi_driver_data exynos5433_dsi_driver_data = { +static const struct exynos_dsi_driver_data exynos5433_dsi_driver_data = { .reg_ofs = exynos5433_reg_ofs, .plltmr_reg = 0xa0, .has_clklane_stop = 1, @@ -482,7 +505,18 @@ static struct exynos_dsi_driver_data exynos5433_dsi_driver_data = { .reg_values = exynos5433_reg_values, }; -static struct of_device_id exynos_dsi_of_match[] = { +static const struct exynos_dsi_driver_data exynos5422_dsi_driver_data = { + .reg_ofs = exynos5433_reg_ofs, + .plltmr_reg = 0xa0, + .has_clklane_stop = 1, + .num_clks = 2, + .max_freq = 1500, + .wait_for_reset = 1, + .num_bits_resol = 12, + .reg_values = exynos5422_reg_values, +}; + +static const struct of_device_id exynos_dsi_of_match[] = { { .compatible = "samsung,exynos3250-mipi-dsi", .data = &exynos3_dsi_driver_data }, { .compatible = "samsung,exynos4210-mipi-dsi", @@ -491,6 +525,8 @@ static struct of_device_id exynos_dsi_of_match[] = { .data = &exynos4415_dsi_driver_data }, { .compatible = "samsung,exynos5410-mipi-dsi", .data = &exynos5_dsi_driver_data }, + { .compatible = "samsung,exynos5422-mipi-dsi", + .data = &exynos5422_dsi_driver_data }, { .compatible = "samsung,exynos5433-mipi-dsi", .data = &exynos5433_dsi_driver_data }, { } @@ -515,10 +551,10 @@ static void exynos_dsi_wait_for_reset(struct exynos_dsi *dsi) static void exynos_dsi_reset(struct exynos_dsi *dsi) { - struct exynos_dsi_driver_data *driver_data = dsi->driver_data; + u32 reset_val = dsi->driver_data->reg_values[RESET_TYPE]; reinit_completion(&dsi->completed); - DSI_WRITE(dsi, DSIM_SWRST_REG, driver_data->reg_values[RESET_TYPE]); + exynos_dsi_write(dsi, DSIM_SWRST_REG, reset_val); } #ifndef MHZ @@ -621,7 +657,7 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, reg |= DSIM_FREQ_BAND(band); } - DSI_WRITE(dsi, DSIM_PLLCTRL_REG, reg); + exynos_dsi_write(dsi, DSIM_PLLCTRL_REG, reg); timeout = 1000; do { @@ -629,7 +665,7 @@ static unsigned long exynos_dsi_set_pll(struct exynos_dsi *dsi, dev_err(dsi->dev, "PLL failed to stabilize\n"); return 0; } - reg = DSI_READ(dsi, DSIM_STATUS_REG); + reg = exynos_dsi_read(dsi, DSIM_STATUS_REG); } while ((reg & DSIM_PLL_STABLE) == 0); return fout; @@ -659,7 +695,7 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi) dev_dbg(dsi->dev, "hs_clk = %lu, byte_clk = %lu, esc_clk = %lu\n", hs_clk, byte_clk, esc_clk); - reg = DSI_READ(dsi, DSIM_CLKCTRL_REG); + reg = exynos_dsi_read(dsi, DSIM_CLKCTRL_REG); reg &= ~(DSIM_ESC_PRESCALER_MASK | DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_PLL_BYPASS | DSIM_BYTE_CLK_SRC_MASK); @@ -669,7 +705,7 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi) | DSIM_LANE_ESC_CLK_EN_DATA(BIT(dsi->lanes) - 1) | DSIM_BYTE_CLK_SRC(0) | DSIM_TX_REQUEST_HSCLK; - DSI_WRITE(dsi, DSIM_CLKCTRL_REG, reg); + exynos_dsi_write(dsi, DSIM_CLKCTRL_REG, reg); return 0; } @@ -677,7 +713,7 @@ static int exynos_dsi_enable_clock(struct exynos_dsi *dsi) static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi) { struct exynos_dsi_driver_data *driver_data = dsi->driver_data; - unsigned int *reg_values = driver_data->reg_values; + const unsigned int *reg_values = driver_data->reg_values; u32 reg; if (driver_data->has_freqband) @@ -686,7 +722,7 @@ static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi) /* B D-PHY: D-PHY Master & Slave Analog Block control */ reg = reg_values[PHYCTRL_ULPS_EXIT] | reg_values[PHYCTRL_VREG_LP] | reg_values[PHYCTRL_SLEW_UP]; - DSI_WRITE(dsi, DSIM_PHYCTRL_REG, reg); + exynos_dsi_write(dsi, DSIM_PHYCTRL_REG, reg); /* * T LPX: Transmitted length of any Low-Power state period @@ -694,7 +730,7 @@ static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi) * burst */ reg = reg_values[PHYTIMING_LPX] | reg_values[PHYTIMING_HS_EXIT]; - DSI_WRITE(dsi, DSIM_PHYTIMING_REG, reg); + exynos_dsi_write(dsi, DSIM_PHYTIMING_REG, reg); /* * T CLK-PREPARE: Time that the transmitter drives the Clock Lane LP-00 @@ -714,7 +750,7 @@ static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi) reg_values[PHYTIMING_CLK_POST] | reg_values[PHYTIMING_CLK_TRAIL]; - DSI_WRITE(dsi, DSIM_PHYTIMING1_REG, reg); + exynos_dsi_write(dsi, DSIM_PHYTIMING1_REG, reg); /* * T HS-PREPARE: Time that the transmitter drives the Data Lane LP-00 @@ -727,29 +763,29 @@ static void exynos_dsi_set_phy_ctrl(struct exynos_dsi *dsi) */ reg = reg_values[PHYTIMING_HS_PREPARE] | reg_values[PHYTIMING_HS_ZERO] | reg_values[PHYTIMING_HS_TRAIL]; - DSI_WRITE(dsi, DSIM_PHYTIMING2_REG, reg); + exynos_dsi_write(dsi, DSIM_PHYTIMING2_REG, reg); } static void exynos_dsi_disable_clock(struct exynos_dsi *dsi) { u32 reg; - reg = DSI_READ(dsi, DSIM_CLKCTRL_REG); + reg = exynos_dsi_read(dsi, DSIM_CLKCTRL_REG); reg &= ~(DSIM_LANE_ESC_CLK_EN_CLK | DSIM_LANE_ESC_CLK_EN_DATA_MASK | DSIM_ESC_CLKEN | DSIM_BYTE_CLKEN); - DSI_WRITE(dsi, DSIM_CLKCTRL_REG, reg); + exynos_dsi_write(dsi, DSIM_CLKCTRL_REG, reg); - reg = DSI_READ(dsi, DSIM_PLLCTRL_REG); + reg = exynos_dsi_read(dsi, DSIM_PLLCTRL_REG); reg &= ~DSIM_PLL_EN; - DSI_WRITE(dsi, DSIM_PLLCTRL_REG, reg); + exynos_dsi_write(dsi, DSIM_PLLCTRL_REG, reg); } static void exynos_dsi_enable_lane(struct exynos_dsi *dsi, u32 lane) { - u32 reg = DSI_READ(dsi, DSIM_CONFIG_REG); + u32 reg = exynos_dsi_read(dsi, DSIM_CONFIG_REG); reg |= (DSIM_NUM_OF_DATA_LANE(dsi->lanes - 1) | DSIM_LANE_EN_CLK | DSIM_LANE_EN(lane)); - DSI_WRITE(dsi, DSIM_CONFIG_REG, reg); + exynos_dsi_write(dsi, DSIM_CONFIG_REG, reg); } static int exynos_dsi_init_link(struct exynos_dsi *dsi) @@ -760,14 +796,14 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi) u32 lanes_mask; /* Initialize FIFO pointers */ - reg = DSI_READ(dsi, DSIM_FIFOCTRL_REG); + reg = exynos_dsi_read(dsi, DSIM_FIFOCTRL_REG); reg &= ~0x1f; - DSI_WRITE(dsi, DSIM_FIFOCTRL_REG, reg); + exynos_dsi_write(dsi, DSIM_FIFOCTRL_REG, reg); usleep_range(9000, 11000); reg |= 0x1f; - DSI_WRITE(dsi, DSIM_FIFOCTRL_REG, reg); + exynos_dsi_write(dsi, DSIM_FIFOCTRL_REG, reg); usleep_range(9000, 11000); /* DSI configuration */ @@ -836,7 +872,7 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi) dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) { reg |= DSIM_CLKLANE_STOP; } - DSI_WRITE(dsi, DSIM_CONFIG_REG, reg); + exynos_dsi_write(dsi, DSIM_CONFIG_REG, reg); lanes_mask = BIT(dsi->lanes) - 1; exynos_dsi_enable_lane(dsi, lanes_mask); @@ -849,19 +885,19 @@ static int exynos_dsi_init_link(struct exynos_dsi *dsi) return -EFAULT; } - reg = DSI_READ(dsi, DSIM_STATUS_REG); + reg = exynos_dsi_read(dsi, DSIM_STATUS_REG); if ((reg & DSIM_STOP_STATE_DAT(lanes_mask)) != DSIM_STOP_STATE_DAT(lanes_mask)) continue; } while (!(reg & (DSIM_STOP_STATE_CLK | DSIM_TX_READY_HS_CLK))); - reg = DSI_READ(dsi, DSIM_ESCMODE_REG); + reg = exynos_dsi_read(dsi, DSIM_ESCMODE_REG); reg &= ~DSIM_STOP_STATE_CNT_MASK; reg |= DSIM_STOP_STATE_CNT(driver_data->reg_values[STOP_STATE_CNT]); - DSI_WRITE(dsi, DSIM_ESCMODE_REG, reg); + exynos_dsi_write(dsi, DSIM_ESCMODE_REG, reg); reg = DSIM_BTA_TIMEOUT(0xff) | DSIM_LPDR_TIMEOUT(0xffff); - DSI_WRITE(dsi, DSIM_TIMEOUT_REG, reg); + exynos_dsi_write(dsi, DSIM_TIMEOUT_REG, reg); return 0; } @@ -876,20 +912,20 @@ static void exynos_dsi_set_display_mode(struct exynos_dsi *dsi) reg = DSIM_CMD_ALLOW(0xf) | DSIM_STABLE_VFP(vm->vfront_porch) | DSIM_MAIN_VBP(vm->vback_porch); - DSI_WRITE(dsi, DSIM_MVPORCH_REG, reg); + exynos_dsi_write(dsi, DSIM_MVPORCH_REG, reg); reg = DSIM_MAIN_HFP(vm->hfront_porch) | DSIM_MAIN_HBP(vm->hback_porch); - DSI_WRITE(dsi, DSIM_MHPORCH_REG, reg); + exynos_dsi_write(dsi, DSIM_MHPORCH_REG, reg); reg = DSIM_MAIN_VSA(vm->vsync_len) | DSIM_MAIN_HSA(vm->hsync_len); - DSI_WRITE(dsi, DSIM_MSYNC_REG, reg); + exynos_dsi_write(dsi, DSIM_MSYNC_REG, reg); } reg = DSIM_MAIN_HRESOL(vm->hactive, num_bits_resol) | DSIM_MAIN_VRESOL(vm->vactive, num_bits_resol); - DSI_WRITE(dsi, DSIM_MDRESOL_REG, reg); + exynos_dsi_write(dsi, DSIM_MDRESOL_REG, reg); dev_dbg(dsi->dev, "LCD size = %dx%d\n", vm->hactive, vm->vactive); } @@ -898,12 +934,12 @@ static void exynos_dsi_set_display_enable(struct exynos_dsi *dsi, bool enable) { u32 reg; - reg = DSI_READ(dsi, DSIM_MDRESOL_REG); + reg = exynos_dsi_read(dsi, DSIM_MDRESOL_REG); if (enable) reg |= DSIM_MAIN_STAND_BY; else reg &= ~DSIM_MAIN_STAND_BY; - DSI_WRITE(dsi, DSIM_MDRESOL_REG, reg); + exynos_dsi_write(dsi, DSIM_MDRESOL_REG, reg); } static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi) @@ -911,7 +947,7 @@ static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi) int timeout = 2000; do { - u32 reg = DSI_READ(dsi, DSIM_FIFOCTRL_REG); + u32 reg = exynos_dsi_read(dsi, DSIM_FIFOCTRL_REG); if (!(reg & DSIM_SFR_HEADER_FULL)) return 0; @@ -925,34 +961,35 @@ static int exynos_dsi_wait_for_hdr_fifo(struct exynos_dsi *dsi) static void exynos_dsi_set_cmd_lpm(struct exynos_dsi *dsi, bool lpm) { - u32 v = DSI_READ(dsi, DSIM_ESCMODE_REG); + u32 v = exynos_dsi_read(dsi, DSIM_ESCMODE_REG); if (lpm) v |= DSIM_CMD_LPDT_LP; else v &= ~DSIM_CMD_LPDT_LP; - DSI_WRITE(dsi, DSIM_ESCMODE_REG, v); + exynos_dsi_write(dsi, DSIM_ESCMODE_REG, v); } static void exynos_dsi_force_bta(struct exynos_dsi *dsi) { - u32 v = DSI_READ(dsi, DSIM_ESCMODE_REG); + u32 v = exynos_dsi_read(dsi, DSIM_ESCMODE_REG); v |= DSIM_FORCE_BTA; - DSI_WRITE(dsi, DSIM_ESCMODE_REG, v); + exynos_dsi_write(dsi, DSIM_ESCMODE_REG, v); } static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi, struct exynos_dsi_transfer *xfer) { struct device *dev = dsi->dev; - const u8 *payload = xfer->tx_payload + xfer->tx_done; - u16 length = xfer->tx_len - xfer->tx_done; + struct mipi_dsi_packet *pkt = &xfer->packet; + const u8 *payload = pkt->payload + xfer->tx_done; + u16 length = pkt->payload_length - xfer->tx_done; bool first = !xfer->tx_done; u32 reg; dev_dbg(dev, "< xfer %p: tx len %u, done %u, rx len %u, done %u\n", - xfer, xfer->tx_len, xfer->tx_done, xfer->rx_len, xfer->rx_done); + xfer, length, xfer->tx_done, xfer->rx_len, xfer->rx_done); if (length > DSI_TX_FIFO_SIZE) length = DSI_TX_FIFO_SIZE; @@ -961,9 +998,8 @@ static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi, /* Send payload */ while (length >= 4) { - reg = (payload[3] << 24) | (payload[2] << 16) - | (payload[1] << 8) | payload[0]; - DSI_WRITE(dsi, DSIM_PAYLOAD_REG, reg); + reg = get_unaligned_le32(payload); + exynos_dsi_write(dsi, DSIM_PAYLOAD_REG, reg); payload += 4; length -= 4; } @@ -978,10 +1014,7 @@ static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi, /* Fall through */ case 1: reg |= payload[0]; - DSI_WRITE(dsi, DSIM_PAYLOAD_REG, reg); - break; - case 0: - /* Do nothing */ + exynos_dsi_write(dsi, DSIM_PAYLOAD_REG, reg); break; } @@ -989,7 +1022,7 @@ static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi, if (!first) return; - reg = (xfer->data[1] << 16) | (xfer->data[0] << 8) | xfer->data_id; + reg = get_unaligned_le32(pkt->header); if (exynos_dsi_wait_for_hdr_fifo(dsi)) { dev_err(dev, "waiting for header FIFO timed out\n"); return; @@ -1001,7 +1034,7 @@ static void exynos_dsi_send_to_fifo(struct exynos_dsi *dsi, dsi->state ^= DSIM_STATE_CMD_LPM; } - DSI_WRITE(dsi, DSIM_PKTHDR_REG, reg); + exynos_dsi_write(dsi, DSIM_PKTHDR_REG, reg); if (xfer->flags & MIPI_DSI_MSG_REQ_ACK) exynos_dsi_force_bta(dsi); @@ -1017,7 +1050,7 @@ static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi, u32 reg; if (first) { - reg = DSI_READ(dsi, DSIM_RXFIFO_REG); + reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG); switch (reg & 0x3f) { case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: @@ -1056,7 +1089,7 @@ static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi, /* Receive payload */ while (length >= 4) { - reg = DSI_READ(dsi, DSIM_RXFIFO_REG); + reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG); payload[0] = (reg >> 0) & 0xff; payload[1] = (reg >> 8) & 0xff; payload[2] = (reg >> 16) & 0xff; @@ -1066,7 +1099,7 @@ static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi, } if (length) { - reg = DSI_READ(dsi, DSIM_RXFIFO_REG); + reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG); switch (length) { case 3: payload[2] = (reg >> 16) & 0xff; @@ -1085,7 +1118,7 @@ static void exynos_dsi_read_from_fifo(struct exynos_dsi *dsi, clear_fifo: length = DSI_RX_FIFO_SIZE / 4; do { - reg = DSI_READ(dsi, DSIM_RXFIFO_REG); + reg = exynos_dsi_read(dsi, DSIM_RXFIFO_REG); if (reg == DSI_RX_FIFO_EMPTY) break; } while (--length); @@ -1110,13 +1143,14 @@ again: spin_unlock_irqrestore(&dsi->transfer_lock, flags); - if (xfer->tx_len && xfer->tx_done == xfer->tx_len) + if (xfer->packet.payload_length && + xfer->tx_done == xfer->packet.payload_length) /* waiting for RX */ return; exynos_dsi_send_to_fifo(dsi, xfer); - if (xfer->tx_len || xfer->rx_len) + if (xfer->packet.payload_length || xfer->rx_len) return; xfer->result = 0; @@ -1152,10 +1186,11 @@ static bool exynos_dsi_transfer_finish(struct exynos_dsi *dsi) spin_unlock_irqrestore(&dsi->transfer_lock, flags); dev_dbg(dsi->dev, - "> xfer %p, tx_len %u, tx_done %u, rx_len %u, rx_done %u\n", - xfer, xfer->tx_len, xfer->tx_done, xfer->rx_len, xfer->rx_done); + "> xfer %p, tx_len %zu, tx_done %u, rx_len %u, rx_done %u\n", + xfer, xfer->packet.payload_length, xfer->tx_done, xfer->rx_len, + xfer->rx_done); - if (xfer->tx_done != xfer->tx_len) + if (xfer->tx_done != xfer->packet.payload_length) return true; if (xfer->rx_done != xfer->rx_len) @@ -1226,9 +1261,10 @@ static int exynos_dsi_transfer(struct exynos_dsi *dsi, wait_for_completion_timeout(&xfer->completed, msecs_to_jiffies(DSI_XFER_TIMEOUT_MS)); if (xfer->result == -ETIMEDOUT) { + struct mipi_dsi_packet *pkt = &xfer->packet; exynos_dsi_remove_transfer(dsi, xfer); - dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 2, xfer->data, - xfer->tx_len, xfer->tx_payload); + dev_err(dsi->dev, "xfer timed out: %*ph %*ph\n", 4, pkt->header, + (int)pkt->payload_length, pkt->payload); return -ETIMEDOUT; } @@ -1241,20 +1277,20 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id) struct exynos_dsi *dsi = dev_id; u32 status; - status = DSI_READ(dsi, DSIM_INTSRC_REG); + status = exynos_dsi_read(dsi, DSIM_INTSRC_REG); if (!status) { static unsigned long int j; if (printk_timed_ratelimit(&j, 500)) dev_warn(dsi->dev, "spurious interrupt\n"); return IRQ_HANDLED; } - DSI_WRITE(dsi, DSIM_INTSRC_REG, status); + exynos_dsi_write(dsi, DSIM_INTSRC_REG, status); if (status & DSIM_INT_SW_RST_RELEASE) { u32 mask = ~(DSIM_INT_RX_DONE | DSIM_INT_SFR_FIFO_EMPTY | DSIM_INT_SFR_HDR_FIFO_EMPTY | DSIM_INT_FRAME_DONE | DSIM_INT_RX_ECC_ERR | DSIM_INT_SW_RST_RELEASE); - DSI_WRITE(dsi, DSIM_INTMSK_REG, mask); + exynos_dsi_write(dsi, DSIM_INTMSK_REG, mask); complete(&dsi->completed); return IRQ_HANDLED; } @@ -1401,12 +1437,6 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host, return 0; } -/* distinguish between short and long DSI packet types */ -static bool exynos_dsi_is_short_dsi_type(u8 type) -{ - return (type & 0x0f) <= 8; -} - static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host, const struct mipi_dsi_msg *msg) { @@ -1424,25 +1454,9 @@ static ssize_t exynos_dsi_host_transfer(struct mipi_dsi_host *host, dsi->state |= DSIM_STATE_INITIALIZED; } - if (msg->tx_len == 0) - return -EINVAL; - - xfer.data_id = msg->type | (msg->channel << 6); - - if (exynos_dsi_is_short_dsi_type(msg->type)) { - const char *tx_buf = msg->tx_buf; - - if (msg->tx_len > 2) - return -EINVAL; - xfer.tx_len = 0; - xfer.data[0] = tx_buf[0]; - xfer.data[1] = (msg->tx_len == 2) ? tx_buf[1] : 0; - } else { - xfer.tx_len = msg->tx_len; - xfer.data[0] = msg->tx_len & 0xff; - xfer.data[1] = msg->tx_len >> 8; - xfer.tx_payload = msg->tx_buf; - } + ret = mipi_dsi_create_packet(&xfer.packet, msg); + if (ret < 0) + return ret; xfer.rx_len = msg->rx_len; xfer.rx_payload = msg->rx_buf; @@ -1774,6 +1788,7 @@ static int exynos_dsi_bind(struct device *dev, struct device *master, bridge = of_drm_find_bridge(dsi->bridge_node); if (bridge) { + encoder->bridge = bridge; drm_bridge_attach(drm_dev, bridge); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index f6118baa8e3e..4ae860c44f1d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -50,7 +50,7 @@ static int exynos_drm_fb_mmap(struct fb_info *info, if (vm_size > exynos_gem->size) return -EINVAL; - ret = dma_mmap_attrs(helper->dev->dev, vma, exynos_gem->pages, + ret = dma_mmap_attrs(to_dma_dev(helper->dev), vma, exynos_gem->cookie, exynos_gem->dma_addr, exynos_gem->size, &exynos_gem->dma_attrs); if (ret < 0) { diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c index c747824f3c98..0525c56145db 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -163,7 +163,6 @@ struct fimc_context { u32 clk_frequency; struct regmap *sysreg; struct fimc_scaler sc; - struct exynos_drm_ipp_pol pol; int id; int irq; bool suspended; @@ -260,32 +259,6 @@ static void fimc_set_type_ctrl(struct fimc_context *ctx, enum fimc_wb wb) fimc_write(ctx, cfg, EXYNOS_CIGCTRL); } -static void fimc_set_polarity(struct fimc_context *ctx, - struct exynos_drm_ipp_pol *pol) -{ - u32 cfg; - - DRM_DEBUG_KMS("inv_pclk[%d]inv_vsync[%d]\n", - pol->inv_pclk, pol->inv_vsync); - DRM_DEBUG_KMS("inv_href[%d]inv_hsync[%d]\n", - pol->inv_href, pol->inv_hsync); - - cfg = fimc_read(ctx, EXYNOS_CIGCTRL); - cfg &= ~(EXYNOS_CIGCTRL_INVPOLPCLK | EXYNOS_CIGCTRL_INVPOLVSYNC | - EXYNOS_CIGCTRL_INVPOLHREF | EXYNOS_CIGCTRL_INVPOLHSYNC); - - if (pol->inv_pclk) - cfg |= EXYNOS_CIGCTRL_INVPOLPCLK; - if (pol->inv_vsync) - cfg |= EXYNOS_CIGCTRL_INVPOLVSYNC; - if (pol->inv_href) - cfg |= EXYNOS_CIGCTRL_INVPOLHREF; - if (pol->inv_hsync) - cfg |= EXYNOS_CIGCTRL_INVPOLHSYNC; - - fimc_write(ctx, cfg, EXYNOS_CIGCTRL); -} - static void fimc_handle_jpeg(struct fimc_context *ctx, bool enable) { u32 cfg; @@ -1467,7 +1440,6 @@ static int fimc_ippdrv_start(struct device *dev, enum drm_exynos_ipp_cmd cmd) /* If set ture, we can save jpeg about screen */ fimc_handle_jpeg(ctx, false); fimc_set_scaler(ctx, &ctx->sc); - fimc_set_polarity(ctx, &ctx->pol); switch (cmd) { case IPP_CMD_M2M: @@ -1723,7 +1695,7 @@ static int fimc_probe(struct platform_device *pdev) goto err_put_clk; } - DRM_DEBUG_KMS("id[%d]ippdrv[0x%x]\n", ctx->id, (int)ippdrv); + DRM_DEBUG_KMS("id[%d]ippdrv[%p]\n", ctx->id, ippdrv); spin_lock_init(&ctx->lock); platform_set_drvdata(pdev, ctx); diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 70194d0e4fe4..51d484ae9f49 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -94,12 +94,14 @@ struct fimd_driver_data { unsigned int lcdblk_offset; unsigned int lcdblk_vt_shift; unsigned int lcdblk_bypass_shift; + unsigned int lcdblk_mic_bypass_shift; unsigned int has_shadowcon:1; unsigned int has_clksel:1; unsigned int has_limited_fmt:1; unsigned int has_vidoutcon:1; unsigned int has_vtsel:1; + unsigned int has_mic_bypass:1; }; static struct fimd_driver_data s3c64xx_fimd_driver_data = { @@ -145,6 +147,18 @@ static struct fimd_driver_data exynos5_fimd_driver_data = { .has_vtsel = 1, }; +static struct fimd_driver_data exynos5420_fimd_driver_data = { + .timing_base = 0x20000, + .lcdblk_offset = 0x214, + .lcdblk_vt_shift = 24, + .lcdblk_bypass_shift = 15, + .lcdblk_mic_bypass_shift = 11, + .has_shadowcon = 1, + .has_vidoutcon = 1, + .has_vtsel = 1, + .has_mic_bypass = 1, +}; + struct fimd_context { struct device *dev; struct drm_device *drm_dev; @@ -168,7 +182,6 @@ struct fimd_context { atomic_t win_updated; atomic_t triggering; - struct exynos_drm_panel_info panel; struct fimd_driver_data *driver_data; struct drm_encoder *encoder; }; @@ -184,6 +197,8 @@ static const struct of_device_id fimd_driver_dt_match[] = { .data = &exynos4415_fimd_driver_data }, { .compatible = "samsung,exynos5250-fimd", .data = &exynos5_fimd_driver_data }, + { .compatible = "samsung,exynos5420-fimd", + .data = &exynos5420_fimd_driver_data }, {}, }; MODULE_DEVICE_TABLE(of, fimd_driver_dt_match); @@ -380,7 +395,7 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx, } /* Find the clock divider value that gets us closest to ideal_clk */ - clkdiv = DIV_ROUND_UP(clk_get_rate(ctx->lcd_clk), ideal_clk); + clkdiv = DIV_ROUND_CLOSEST(clk_get_rate(ctx->lcd_clk), ideal_clk); return (clkdiv < 0x100) ? clkdiv : 0xff; } @@ -461,6 +476,18 @@ static void fimd_commit(struct exynos_drm_crtc *crtc) return; } + /* TODO: When MIC is enabled for display path, the lcdblk_mic_bypass + * bit should be cleared. + */ + if (driver_data->has_mic_bypass && ctx->sysreg && + regmap_update_bits(ctx->sysreg, + driver_data->lcdblk_offset, + 0x1 << driver_data->lcdblk_mic_bypass_shift, + 0x1 << driver_data->lcdblk_mic_bypass_shift)) { + DRM_ERROR("Failed to update sysreg for bypass mic.\n"); + return; + } + /* setup horizontal and vertical display size. */ val = VIDTCON2_LINEVAL(mode->vdisplay - 1) | VIDTCON2_HOZVAL(mode->hdisplay - 1) | @@ -861,7 +888,8 @@ static void fimd_dp_clock_enable(struct exynos_drm_crtc *crtc, bool enable) * clock. On these SoCs the bootloader may enable it but any * power domain off/on will reset it to disable state. */ - if (ctx->driver_data != &exynos5_fimd_driver_data) + if (ctx->driver_data != &exynos5_fimd_driver_data || + ctx->driver_data != &exynos5420_fimd_driver_data) return; val = enable ? DP_MIE_CLK_DP_ENABLE : DP_MIE_CLK_DISABLE; diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 99369816ff97..193d3602dffb 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -259,7 +259,7 @@ static int g2d_init_cmdlist(struct g2d_data *g2d) init_dma_attrs(&g2d->cmdlist_dma_attrs); dma_set_attr(DMA_ATTR_WRITE_COMBINE, &g2d->cmdlist_dma_attrs); - g2d->cmdlist_pool_virt = dma_alloc_attrs(subdrv->drm_dev->dev, + g2d->cmdlist_pool_virt = dma_alloc_attrs(to_dma_dev(subdrv->drm_dev), G2D_CMDLIST_POOL_SIZE, &g2d->cmdlist_pool, GFP_KERNEL, &g2d->cmdlist_dma_attrs); @@ -293,7 +293,7 @@ static int g2d_init_cmdlist(struct g2d_data *g2d) return 0; err: - dma_free_attrs(subdrv->drm_dev->dev, G2D_CMDLIST_POOL_SIZE, + dma_free_attrs(to_dma_dev(subdrv->drm_dev), G2D_CMDLIST_POOL_SIZE, g2d->cmdlist_pool_virt, g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs); return ret; @@ -306,7 +306,8 @@ static void g2d_fini_cmdlist(struct g2d_data *g2d) kfree(g2d->cmdlist_node); if (g2d->cmdlist_pool_virt && g2d->cmdlist_pool) { - dma_free_attrs(subdrv->drm_dev->dev, G2D_CMDLIST_POOL_SIZE, + dma_free_attrs(to_dma_dev(subdrv->drm_dev), + G2D_CMDLIST_POOL_SIZE, g2d->cmdlist_pool_virt, g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs); } @@ -1151,7 +1152,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, goto err_free_event; } - cmd = (struct drm_exynos_g2d_cmd *)(uint32_t)req->cmd; + cmd = (struct drm_exynos_g2d_cmd *)(unsigned long)req->cmd; if (copy_from_user(cmdlist->data + cmdlist->last, (void __user *)cmd, @@ -1169,7 +1170,8 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, if (req->cmd_buf_nr) { struct drm_exynos_g2d_cmd *cmd_buf; - cmd_buf = (struct drm_exynos_g2d_cmd *)(uint32_t)req->cmd_buf; + cmd_buf = (struct drm_exynos_g2d_cmd *) + (unsigned long)req->cmd_buf; if (copy_from_user(cmdlist->data + cmdlist->last, (void __user *)cmd_buf, diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 32358c5e3db4..54b639497d23 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -65,7 +65,7 @@ static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem) return -ENOMEM; } - exynos_gem->cookie = dma_alloc_attrs(dev->dev, exynos_gem->size, + exynos_gem->cookie = dma_alloc_attrs(to_dma_dev(dev), exynos_gem->size, &exynos_gem->dma_addr, GFP_KERNEL, &exynos_gem->dma_attrs); if (!exynos_gem->cookie) { @@ -73,7 +73,7 @@ static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem) goto err_free; } - ret = dma_get_sgtable_attrs(dev->dev, &sgt, exynos_gem->cookie, + ret = dma_get_sgtable_attrs(to_dma_dev(dev), &sgt, exynos_gem->cookie, exynos_gem->dma_addr, exynos_gem->size, &exynos_gem->dma_attrs); if (ret < 0) { @@ -98,7 +98,7 @@ static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem) err_sgt_free: sg_free_table(&sgt); err_dma_free: - dma_free_attrs(dev->dev, exynos_gem->size, exynos_gem->cookie, + dma_free_attrs(to_dma_dev(dev), exynos_gem->size, exynos_gem->cookie, exynos_gem->dma_addr, &exynos_gem->dma_attrs); err_free: drm_free_large(exynos_gem->pages); @@ -118,7 +118,7 @@ static void exynos_drm_free_buf(struct exynos_drm_gem *exynos_gem) DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n", (unsigned long)exynos_gem->dma_addr, exynos_gem->size); - dma_free_attrs(dev->dev, exynos_gem->size, exynos_gem->cookie, + dma_free_attrs(to_dma_dev(dev), exynos_gem->size, exynos_gem->cookie, (dma_addr_t)exynos_gem->dma_addr, &exynos_gem->dma_attrs); @@ -218,7 +218,7 @@ static struct exynos_drm_gem *exynos_drm_gem_init(struct drm_device *dev, return ERR_PTR(ret); } - DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp); + DRM_DEBUG_KMS("created file object = %p\n", obj->filp); return exynos_gem; } @@ -335,7 +335,7 @@ static int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem *exynos_gem, if (vm_size > exynos_gem->size) return -EINVAL; - ret = dma_mmap_attrs(drm_dev->dev, vma, exynos_gem->pages, + ret = dma_mmap_attrs(to_dma_dev(drm_dev), vma, exynos_gem->cookie, exynos_gem->dma_addr, exynos_gem->size, &exynos_gem->dma_attrs); if (ret < 0) { @@ -381,7 +381,7 @@ int exynos_gem_map_sgt_with_dma(struct drm_device *drm_dev, mutex_lock(&drm_dev->struct_mutex); - nents = dma_map_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir); + nents = dma_map_sg(to_dma_dev(drm_dev), sgt->sgl, sgt->nents, dir); if (!nents) { DRM_ERROR("failed to map sgl with dma.\n"); mutex_unlock(&drm_dev->struct_mutex); @@ -396,7 +396,7 @@ void exynos_gem_unmap_sgt_from_dma(struct drm_device *drm_dev, struct sg_table *sgt, enum dma_data_direction dir) { - dma_unmap_sg(drm_dev->dev, sgt->sgl, sgt->nents, dir); + dma_unmap_sg(to_dma_dev(drm_dev), sgt->sgl, sgt->nents, dir); } void exynos_drm_gem_free_object(struct drm_gem_object *obj) diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c index 7aecd23cfa11..5d20da8f957e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -1723,7 +1723,7 @@ static int gsc_probe(struct platform_device *pdev) return ret; } - DRM_DEBUG_KMS("id[%d]ippdrv[0x%x]\n", ctx->id, (int)ippdrv); + DRM_DEBUG_KMS("id[%d]ippdrv[%p]\n", ctx->id, ippdrv); mutex_init(&ctx->lock); platform_set_drvdata(pdev, ctx); diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.c b/drivers/gpu/drm/exynos/exynos_drm_iommu.c index d73b9ad35b7a..7ca09ee19656 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.c +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.c @@ -9,7 +9,7 @@ * option) any later version. */ -#include <drmP.h> +#include <drm/drmP.h> #include <drm/exynos_drm.h> #include <linux/dma-mapping.h> @@ -30,7 +30,6 @@ int drm_create_iommu_mapping(struct drm_device *drm_dev) { struct dma_iommu_mapping *mapping = NULL; struct exynos_drm_private *priv = drm_dev->dev_private; - struct device *dev = drm_dev->dev; if (!priv->da_start) priv->da_start = EXYNOS_DEV_ADDR_START; @@ -43,18 +42,9 @@ int drm_create_iommu_mapping(struct drm_device *drm_dev) if (IS_ERR(mapping)) return PTR_ERR(mapping); - dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms), - GFP_KERNEL); - if (!dev->dma_parms) - goto error; - - dma_set_max_seg_size(dev, 0xffffffffu); - dev->archdata.mapping = mapping; + priv->mapping = mapping; return 0; -error: - arm_iommu_release_mapping(mapping); - return -ENOMEM; } /* @@ -67,9 +57,9 @@ error: */ void drm_release_iommu_mapping(struct drm_device *drm_dev) { - struct device *dev = drm_dev->dev; + struct exynos_drm_private *priv = drm_dev->dev_private; - arm_iommu_release_mapping(dev->archdata.mapping); + arm_iommu_release_mapping(priv->mapping); } /* @@ -84,10 +74,10 @@ void drm_release_iommu_mapping(struct drm_device *drm_dev) int drm_iommu_attach_device(struct drm_device *drm_dev, struct device *subdrv_dev) { - struct device *dev = drm_dev->dev; + struct exynos_drm_private *priv = drm_dev->dev_private; int ret; - if (!dev->archdata.mapping) + if (!priv->mapping) return 0; subdrv_dev->dma_parms = devm_kzalloc(subdrv_dev, @@ -101,23 +91,12 @@ int drm_iommu_attach_device(struct drm_device *drm_dev, if (subdrv_dev->archdata.mapping) arm_iommu_detach_device(subdrv_dev); - ret = arm_iommu_attach_device(subdrv_dev, dev->archdata.mapping); + ret = arm_iommu_attach_device(subdrv_dev, priv->mapping); if (ret < 0) { DRM_DEBUG_KMS("failed iommu attach.\n"); return ret; } - /* - * Set dma_ops to drm_device just one time. - * - * The dma mapping api needs device object and the api is used - * to allocate physial memory and map it with iommu table. - * If iommu attach succeeded, the sub driver would have dma_ops - * for iommu and also all sub drivers have same dma_ops. - */ - if (get_dma_ops(dev) == get_dma_ops(NULL)) - set_dma_ops(dev, get_dma_ops(subdrv_dev)); - return 0; } @@ -133,8 +112,8 @@ int drm_iommu_attach_device(struct drm_device *drm_dev, void drm_iommu_detach_device(struct drm_device *drm_dev, struct device *subdrv_dev) { - struct device *dev = drm_dev->dev; - struct dma_iommu_mapping *mapping = dev->archdata.mapping; + struct exynos_drm_private *priv = drm_dev->dev_private; + struct dma_iommu_mapping *mapping = priv->mapping; if (!mapping || !mapping->domain) return; diff --git a/drivers/gpu/drm/exynos/exynos_drm_iommu.h b/drivers/gpu/drm/exynos/exynos_drm_iommu.h index dc1b5441f491..5ffebe02ee4d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_iommu.h +++ b/drivers/gpu/drm/exynos/exynos_drm_iommu.h @@ -29,9 +29,9 @@ void drm_iommu_detach_device(struct drm_device *dev_dev, static inline bool is_drm_iommu_supported(struct drm_device *drm_dev) { - struct device *dev = drm_dev->dev; + struct exynos_drm_private *priv = drm_dev->dev_private; - return dev->archdata.mapping ? true : false; + return priv->mapping ? true : false; } #else diff --git a/drivers/gpu/drm/exynos/exynos_drm_ipp.c b/drivers/gpu/drm/exynos/exynos_drm_ipp.c index 3eab0d15f0b4..9c84ee76f18a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_ipp.c +++ b/drivers/gpu/drm/exynos/exynos_drm_ipp.c @@ -208,7 +208,7 @@ static struct exynos_drm_ippdrv *ipp_find_drv_by_handle(u32 prop_id) * e.g PAUSE state, queue buf, command control. */ list_for_each_entry(ippdrv, &exynos_drm_ippdrv_list, drv_list) { - DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n", count++, (int)ippdrv); + DRM_DEBUG_KMS("count[%d]ippdrv[%p]\n", count++, ippdrv); mutex_lock(&ippdrv->cmd_lock); list_for_each_entry(c_node, &ippdrv->cmd_list, list) { @@ -388,8 +388,8 @@ int exynos_drm_ipp_set_property(struct drm_device *drm_dev, void *data, } property->prop_id = ret; - DRM_DEBUG_KMS("created prop_id[%d]cmd[%d]ippdrv[0x%x]\n", - property->prop_id, property->cmd, (int)ippdrv); + DRM_DEBUG_KMS("created prop_id[%d]cmd[%d]ippdrv[%p]\n", + property->prop_id, property->cmd, ippdrv); /* stored property information and ippdrv in private data */ c_node->property = *property; @@ -518,7 +518,7 @@ static int ipp_put_mem_node(struct drm_device *drm_dev, { int i; - DRM_DEBUG_KMS("node[0x%x]\n", (int)m_node); + DRM_DEBUG_KMS("node[%p]\n", m_node); if (!m_node) { DRM_ERROR("invalid dequeue node.\n"); @@ -562,7 +562,7 @@ static struct drm_exynos_ipp_mem_node m_node->buf_id = qbuf->buf_id; INIT_LIST_HEAD(&m_node->list); - DRM_DEBUG_KMS("m_node[0x%x]ops_id[%d]\n", (int)m_node, qbuf->ops_id); + DRM_DEBUG_KMS("m_node[%p]ops_id[%d]\n", m_node, qbuf->ops_id); DRM_DEBUG_KMS("prop_id[%d]buf_id[%d]\n", qbuf->prop_id, m_node->buf_id); for_each_ipp_planar(i) { @@ -582,8 +582,8 @@ static struct drm_exynos_ipp_mem_node buf_info->handles[i] = qbuf->handle[i]; buf_info->base[i] = *addr; - DRM_DEBUG_KMS("i[%d]base[0x%x]hd[0x%lx]\n", i, - buf_info->base[i], buf_info->handles[i]); + DRM_DEBUG_KMS("i[%d]base[%pad]hd[0x%lx]\n", i, + &buf_info->base[i], buf_info->handles[i]); } } @@ -659,7 +659,7 @@ static void ipp_put_event(struct drm_exynos_ipp_cmd_node *c_node, mutex_lock(&c_node->event_lock); list_for_each_entry_safe(e, te, &c_node->event_list, base.link) { - DRM_DEBUG_KMS("count[%d]e[0x%x]\n", count++, (int)e); + DRM_DEBUG_KMS("count[%d]e[%p]\n", count++, e); /* * qbuf == NULL condition means all event deletion. @@ -750,7 +750,7 @@ static struct drm_exynos_ipp_mem_node /* find memory node from memory list */ list_for_each_entry(m_node, head, list) { - DRM_DEBUG_KMS("count[%d]m_node[0x%x]\n", count++, (int)m_node); + DRM_DEBUG_KMS("count[%d]m_node[%p]\n", count++, m_node); /* compare buffer id */ if (m_node->buf_id == qbuf->buf_id) @@ -767,7 +767,7 @@ static int ipp_set_mem_node(struct exynos_drm_ippdrv *ippdrv, struct exynos_drm_ipp_ops *ops = NULL; int ret = 0; - DRM_DEBUG_KMS("node[0x%x]\n", (int)m_node); + DRM_DEBUG_KMS("node[%p]\n", m_node); if (!m_node) { DRM_ERROR("invalid queue node.\n"); @@ -1232,7 +1232,7 @@ static int ipp_start_property(struct exynos_drm_ippdrv *ippdrv, m_node = list_first_entry(head, struct drm_exynos_ipp_mem_node, list); - DRM_DEBUG_KMS("m_node[0x%x]\n", (int)m_node); + DRM_DEBUG_KMS("m_node[%p]\n", m_node); ret = ipp_set_mem_node(ippdrv, c_node, m_node); if (ret) { @@ -1601,8 +1601,8 @@ static int ipp_subdrv_probe(struct drm_device *drm_dev, struct device *dev) } ippdrv->prop_list.ipp_id = ret; - DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]ipp_id[%d]\n", - count++, (int)ippdrv, ret); + DRM_DEBUG_KMS("count[%d]ippdrv[%p]ipp_id[%d]\n", + count++, ippdrv, ret); /* store parent device for node */ ippdrv->parent_dev = dev; @@ -1659,7 +1659,7 @@ static int ipp_subdrv_open(struct drm_device *drm_dev, struct device *dev, file_priv->ipp_dev = dev; - DRM_DEBUG_KMS("done priv[0x%x]\n", (int)dev); + DRM_DEBUG_KMS("done priv[%p]\n", dev); return 0; } @@ -1676,8 +1676,8 @@ static void ipp_subdrv_close(struct drm_device *drm_dev, struct device *dev, mutex_lock(&ippdrv->cmd_lock); list_for_each_entry_safe(c_node, tc_node, &ippdrv->cmd_list, list) { - DRM_DEBUG_KMS("count[%d]ippdrv[0x%x]\n", - count++, (int)ippdrv); + DRM_DEBUG_KMS("count[%d]ippdrv[%p]\n", + count++, ippdrv); if (c_node->filp == file) { /* diff --git a/drivers/gpu/drm/exynos/exynos_drm_mic.c b/drivers/gpu/drm/exynos/exynos_drm_mic.c index 4eaef36aec5a..9869d70e9e54 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_mic.c +++ b/drivers/gpu/drm/exynos/exynos_drm_mic.c @@ -18,6 +18,7 @@ #include <linux/of.h> #include <linux/of_graph.h> #include <linux/clk.h> +#include <linux/component.h> #include <drm/drmP.h> #include <linux/mfd/syscon.h> #include <linux/regmap.h> @@ -306,9 +307,9 @@ exit: return ret; } -void mic_disable(struct drm_bridge *bridge) { } +static void mic_disable(struct drm_bridge *bridge) { } -void mic_post_disable(struct drm_bridge *bridge) +static void mic_post_disable(struct drm_bridge *bridge) { struct exynos_mic *mic = bridge->driver_private; int i; @@ -328,7 +329,7 @@ already_disabled: mutex_unlock(&mic_mutex); } -void mic_pre_enable(struct drm_bridge *bridge) +static void mic_pre_enable(struct drm_bridge *bridge) { struct exynos_mic *mic = bridge->driver_private; int ret, i; @@ -371,11 +372,35 @@ already_enabled: mutex_unlock(&mic_mutex); } -void mic_enable(struct drm_bridge *bridge) { } +static void mic_enable(struct drm_bridge *bridge) { } -void mic_destroy(struct drm_bridge *bridge) +static const struct drm_bridge_funcs mic_bridge_funcs = { + .disable = mic_disable, + .post_disable = mic_post_disable, + .pre_enable = mic_pre_enable, + .enable = mic_enable, +}; + +static int exynos_mic_bind(struct device *dev, struct device *master, + void *data) { - struct exynos_mic *mic = bridge->driver_private; + struct exynos_mic *mic = dev_get_drvdata(dev); + int ret; + + mic->bridge.funcs = &mic_bridge_funcs; + mic->bridge.of_node = dev->of_node; + mic->bridge.driver_private = mic; + ret = drm_bridge_add(&mic->bridge); + if (ret) + DRM_ERROR("mic: Failed to add MIC to the global bridge list\n"); + + return ret; +} + +static void exynos_mic_unbind(struct device *dev, struct device *master, + void *data) +{ + struct exynos_mic *mic = dev_get_drvdata(dev); int i; mutex_lock(&mic_mutex); @@ -387,16 +412,16 @@ void mic_destroy(struct drm_bridge *bridge) already_disabled: mutex_unlock(&mic_mutex); + + drm_bridge_remove(&mic->bridge); } -static const struct drm_bridge_funcs mic_bridge_funcs = { - .disable = mic_disable, - .post_disable = mic_post_disable, - .pre_enable = mic_pre_enable, - .enable = mic_enable, +static const struct component_ops exynos_mic_component_ops = { + .bind = exynos_mic_bind, + .unbind = exynos_mic_unbind, }; -int exynos_mic_probe(struct platform_device *pdev) +static int exynos_mic_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct exynos_mic *mic; @@ -435,17 +460,8 @@ int exynos_mic_probe(struct platform_device *pdev) goto err; } - mic->bridge.funcs = &mic_bridge_funcs; - mic->bridge.of_node = dev->of_node; - mic->bridge.driver_private = mic; - ret = drm_bridge_add(&mic->bridge); - if (ret) { - DRM_ERROR("mic: Failed to add MIC to the global bridge list\n"); - goto err; - } - for (i = 0; i < NUM_CLKS; i++) { - mic->clks[i] = of_clk_get_by_name(dev->of_node, clk_names[i]); + mic->clks[i] = devm_clk_get(dev, clk_names[i]); if (IS_ERR(mic->clks[i])) { DRM_ERROR("mic: Failed to get clock (%s)\n", clk_names[i]); @@ -454,7 +470,10 @@ int exynos_mic_probe(struct platform_device *pdev) } } + platform_set_drvdata(pdev, mic); + DRM_DEBUG_KMS("MIC has been probed\n"); + return component_add(dev, &exynos_mic_component_ops); err: return ret; @@ -462,14 +481,7 @@ err: static int exynos_mic_remove(struct platform_device *pdev) { - struct exynos_mic *mic = platform_get_drvdata(pdev); - int i; - - drm_bridge_remove(&mic->bridge); - - for (i = NUM_CLKS - 1; i > -1; i--) - clk_put(mic->clks[i]); - + component_del(&pdev->dev, &exynos_mic_component_ops); return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c index bea0f7826d30..f18fbe43f55f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c @@ -20,7 +20,6 @@ #include <drm/drmP.h> #include <drm/exynos_drm.h> #include "regs-rotator.h" -#include "exynos_drm.h" #include "exynos_drm_drv.h" #include "exynos_drm_ipp.h" @@ -754,7 +753,7 @@ static int rotator_probe(struct platform_device *pdev) goto err_ippdrv_register; } - DRM_DEBUG_KMS("ippdrv[0x%x]\n", (int)ippdrv); + DRM_DEBUG_KMS("ippdrv[%p]\n", ippdrv); platform_set_drvdata(pdev, rot); diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 65108cbd79d4..608b0afa337f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -223,7 +223,7 @@ static void vidi_fake_vblank_handler(struct work_struct *work) } } -static int vidi_show_connection(struct device *dev, +static ssize_t vidi_show_connection(struct device *dev, struct device_attribute *attr, char *buf) { struct vidi_context *ctx = dev_get_drvdata(dev); @@ -238,7 +238,7 @@ static int vidi_show_connection(struct device *dev, return rc; } -static int vidi_store_connection(struct device *dev, +static ssize_t vidi_store_connection(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { @@ -294,7 +294,9 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, } if (vidi->connection) { - struct edid *raw_edid = (struct edid *)(uint32_t)vidi->edid; + struct edid *raw_edid; + + raw_edid = (struct edid *)(unsigned long)vidi->edid; if (!drm_edid_is_valid(raw_edid)) { DRM_DEBUG_KMS("edid data is invalid.\n"); return -EINVAL; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 21a29dbce18c..e148d728e28c 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -867,10 +867,8 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata, { u32 hdr_sum; u8 chksum; - u32 mod; u8 ar; - mod = hdmi_reg_read(hdata, HDMI_MODE_SEL); if (hdata->dvi_mode) { hdmi_reg_writeb(hdata, HDMI_VSI_CON, HDMI_VSI_CON_DO_NOT_TRANSMIT); diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c index d8ab8f0af10c..7574db2da413 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c @@ -42,34 +42,24 @@ static void fsl_dcu_drm_disable_crtc(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; - int ret; - ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, - DCU_MODE_DCU_MODE_MASK, - DCU_MODE_DCU_MODE(DCU_MODE_OFF)); - if (ret) - dev_err(fsl_dev->dev, "Disable CRTC failed\n"); - ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, - DCU_UPDATE_MODE_READREG); - if (ret) - dev_err(fsl_dev->dev, "Enable CRTC failed\n"); + regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, + DCU_MODE_DCU_MODE_MASK, + DCU_MODE_DCU_MODE(DCU_MODE_OFF)); + regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, + DCU_UPDATE_MODE_READREG); } static void fsl_dcu_drm_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; - int ret; - ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, - DCU_MODE_DCU_MODE_MASK, - DCU_MODE_DCU_MODE(DCU_MODE_NORMAL)); - if (ret) - dev_err(fsl_dev->dev, "Enable CRTC failed\n"); - ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, - DCU_UPDATE_MODE_READREG); - if (ret) - dev_err(fsl_dev->dev, "Enable CRTC failed\n"); + regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, + DCU_MODE_DCU_MODE_MASK, + DCU_MODE_DCU_MODE(DCU_MODE_NORMAL)); + regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, + DCU_UPDATE_MODE_READREG); } static bool fsl_dcu_drm_crtc_mode_fixup(struct drm_crtc *crtc, @@ -84,9 +74,8 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; struct drm_display_mode *mode = &crtc->state->mode; - unsigned int hbp, hfp, hsw, vbp, vfp, vsw, div, index; + unsigned int hbp, hfp, hsw, vbp, vfp, vsw, div, index, pol = 0; unsigned long dcuclk; - int ret; index = drm_crtc_index(crtc); dcuclk = clk_get_rate(fsl_dev->clk); @@ -100,51 +89,36 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc) vfp = mode->vsync_start - mode->vdisplay; vsw = mode->vsync_end - mode->vsync_start; - ret = regmap_write(fsl_dev->regmap, DCU_HSYN_PARA, - DCU_HSYN_PARA_BP(hbp) | - DCU_HSYN_PARA_PW(hsw) | - DCU_HSYN_PARA_FP(hfp)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_VSYN_PARA, - DCU_VSYN_PARA_BP(vbp) | - DCU_VSYN_PARA_PW(vsw) | - DCU_VSYN_PARA_FP(vfp)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_DISP_SIZE, - DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) | - DCU_DISP_SIZE_DELTA_X(mode->hdisplay)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_DIV_RATIO, div); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_SYN_POL, - DCU_SYN_POL_INV_VS_LOW | DCU_SYN_POL_INV_HS_LOW); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) | - DCU_BGND_G(0) | DCU_BGND_B(0)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_DCU_MODE, - DCU_MODE_BLEND_ITER(1) | DCU_MODE_RASTER_EN); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_THRESHOLD, - DCU_THRESHOLD_LS_BF_VS(BF_VS_VAL) | - DCU_THRESHOLD_OUT_BUF_HIGH(BUF_MAX_VAL) | - DCU_THRESHOLD_OUT_BUF_LOW(BUF_MIN_VAL)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, - DCU_UPDATE_MODE_READREG); - if (ret) - goto set_failed; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + pol |= DCU_SYN_POL_INV_HS_LOW; + + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + pol |= DCU_SYN_POL_INV_VS_LOW; + + regmap_write(fsl_dev->regmap, DCU_HSYN_PARA, + DCU_HSYN_PARA_BP(hbp) | + DCU_HSYN_PARA_PW(hsw) | + DCU_HSYN_PARA_FP(hfp)); + regmap_write(fsl_dev->regmap, DCU_VSYN_PARA, + DCU_VSYN_PARA_BP(vbp) | + DCU_VSYN_PARA_PW(vsw) | + DCU_VSYN_PARA_FP(vfp)); + regmap_write(fsl_dev->regmap, DCU_DISP_SIZE, + DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) | + DCU_DISP_SIZE_DELTA_X(mode->hdisplay)); + regmap_write(fsl_dev->regmap, DCU_DIV_RATIO, div); + regmap_write(fsl_dev->regmap, DCU_SYN_POL, pol); + regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) | + DCU_BGND_G(0) | DCU_BGND_B(0)); + regmap_write(fsl_dev->regmap, DCU_DCU_MODE, + DCU_MODE_BLEND_ITER(1) | DCU_MODE_RASTER_EN); + regmap_write(fsl_dev->regmap, DCU_THRESHOLD, + DCU_THRESHOLD_LS_BF_VS(BF_VS_VAL) | + DCU_THRESHOLD_OUT_BUF_HIGH(BUF_MAX_VAL) | + DCU_THRESHOLD_OUT_BUF_LOW(BUF_MIN_VAL)); + regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, + DCU_UPDATE_MODE_READREG); return; -set_failed: - dev_err(dev->dev, "set DCU register failed\n"); } static const struct drm_crtc_helper_funcs fsl_dcu_drm_crtc_helper_funcs = { @@ -174,10 +148,15 @@ int fsl_dcu_drm_crtc_create(struct fsl_dcu_drm_device *fsl_dev) int ret; primary = fsl_dcu_drm_primary_create_plane(fsl_dev->drm); + if (!primary) + return -ENOMEM; + ret = drm_crtc_init_with_planes(fsl_dev->drm, crtc, primary, NULL, &fsl_dcu_drm_crtc_funcs, NULL); - if (ret < 0) + if (ret) { + primary->funcs->destroy(primary); return ret; + } drm_crtc_helper_add(crtc, &fsl_dcu_drm_crtc_helper_funcs); @@ -185,26 +164,15 @@ int fsl_dcu_drm_crtc_create(struct fsl_dcu_drm_device *fsl_dev) reg_num = LS1021A_LAYER_REG_NUM; else reg_num = VF610_LAYER_REG_NUM; - for (i = 0; i <= fsl_dev->soc->total_layer; i++) { - for (j = 0; j < reg_num; j++) { - ret = regmap_write(fsl_dev->regmap, - DCU_CTRLDESCLN(i, j), 0); - if (ret) - goto init_failed; - } + for (i = 0; i < fsl_dev->soc->total_layer; i++) { + for (j = 1; j <= reg_num; j++) + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(i, j), 0); } - ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, - DCU_MODE_DCU_MODE_MASK, - DCU_MODE_DCU_MODE(DCU_MODE_OFF)); - if (ret) - goto init_failed; - ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, - DCU_UPDATE_MODE_READREG); - if (ret) - goto init_failed; + regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, + DCU_MODE_DCU_MODE_MASK, + DCU_MODE_DCU_MODE(DCU_MODE_OFF)); + regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, + DCU_UPDATE_MODE_READREG); return 0; -init_failed: - dev_err(fsl_dev->dev, "init DCU register failed\n"); - return ret; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index 9648b7f9a31c..e8d9337a66d8 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -28,37 +28,36 @@ #include "fsl_dcu_drm_crtc.h" #include "fsl_dcu_drm_drv.h" +static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg == DCU_INT_STATUS || reg == DCU_UPDATE_MODE) + return true; + + return false; +} + static const struct regmap_config fsl_dcu_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, .cache_type = REGCACHE_RBTREE, + + .volatile_reg = fsl_dcu_drm_is_volatile_reg, }; static int fsl_dcu_drm_irq_init(struct drm_device *dev) { struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; - unsigned int value; int ret; ret = drm_irq_install(dev, fsl_dev->irq); if (ret < 0) dev_err(dev->dev, "failed to install IRQ handler\n"); - ret = regmap_write(fsl_dev->regmap, DCU_INT_STATUS, 0); - if (ret) - dev_err(dev->dev, "set DCU_INT_STATUS failed\n"); - ret = regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value); - if (ret) - dev_err(dev->dev, "read DCU_INT_MASK failed\n"); - value &= DCU_INT_MASK_VBLANK; - ret = regmap_write(fsl_dev->regmap, DCU_INT_MASK, value); - if (ret) - dev_err(dev->dev, "set DCU_INT_MASK failed\n"); - ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, - DCU_UPDATE_MODE_READREG); - if (ret) - dev_err(dev->dev, "set DCU_UPDATE_MODE failed\n"); + regmap_write(fsl_dev->regmap, DCU_INT_STATUS, 0); + regmap_write(fsl_dev->regmap, DCU_INT_MASK, ~0); + regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, + DCU_UPDATE_MODE_READREG); return ret; } @@ -120,18 +119,17 @@ static irqreturn_t fsl_dcu_drm_irq(int irq, void *arg) int ret; ret = regmap_read(fsl_dev->regmap, DCU_INT_STATUS, &int_status); - if (ret) - dev_err(dev->dev, "set DCU_INT_STATUS failed\n"); + if (ret) { + dev_err(dev->dev, "read DCU_INT_STATUS failed\n"); + return IRQ_NONE; + } + if (int_status & DCU_INT_STATUS_VBLANK) drm_handle_vblank(dev, 0); - ret = regmap_write(fsl_dev->regmap, DCU_INT_STATUS, 0xffffffff); - if (ret) - dev_err(dev->dev, "set DCU_INT_STATUS failed\n"); - ret = regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, - DCU_UPDATE_MODE_READREG); - if (ret) - dev_err(dev->dev, "set DCU_UPDATE_MODE failed\n"); + regmap_write(fsl_dev->regmap, DCU_INT_STATUS, int_status); + regmap_write(fsl_dev->regmap, DCU_UPDATE_MODE, + DCU_UPDATE_MODE_READREG); return IRQ_HANDLED; } @@ -140,15 +138,11 @@ static int fsl_dcu_drm_enable_vblank(struct drm_device *dev, unsigned int pipe) { struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; unsigned int value; - int ret; - ret = regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value); - if (ret) - dev_err(dev->dev, "read DCU_INT_MASK failed\n"); + regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value); value &= ~DCU_INT_MASK_VBLANK; - ret = regmap_write(fsl_dev->regmap, DCU_INT_MASK, value); - if (ret) - dev_err(dev->dev, "set DCU_INT_MASK failed\n"); + regmap_write(fsl_dev->regmap, DCU_INT_MASK, value); + return 0; } @@ -157,15 +151,10 @@ static void fsl_dcu_drm_disable_vblank(struct drm_device *dev, { struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; unsigned int value; - int ret; - ret = regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value); - if (ret) - dev_err(dev->dev, "read DCU_INT_MASK failed\n"); + regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value); value |= DCU_INT_MASK_VBLANK; - ret = regmap_write(fsl_dev->regmap, DCU_INT_MASK, value); - if (ret) - dev_err(dev->dev, "set DCU_INT_MASK failed\n"); + regmap_write(fsl_dev->regmap, DCU_INT_MASK, value); } static const struct file_operations fsl_dcu_drm_fops = { diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h index 579b9e44e764..6413ac9e4769 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h @@ -133,7 +133,9 @@ #define DCU_LAYER_RLE_EN BIT(15) #define DCU_LAYER_LUOFFS(x) ((x) << 4) #define DCU_LAYER_BB_ON BIT(2) -#define DCU_LAYER_AB(x) (x) +#define DCU_LAYER_AB_NONE 0 +#define DCU_LAYER_AB_CHROMA_KEYING 1 +#define DCU_LAYER_AB_WHOLE_FRAME 2 #define DCU_LAYER_CKMAX_R(x) ((x) << 16) #define DCU_LAYER_CKMAX_G(x) ((x) << 8) diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c index 0ef5959710e7..c564ec612b59 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_kms.c @@ -25,6 +25,8 @@ static const struct drm_mode_config_funcs fsl_dcu_drm_mode_config_funcs = { int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev) { + int ret; + drm_mode_config_init(fsl_dev->drm); fsl_dev->drm->mode_config.min_width = 0; @@ -33,11 +35,25 @@ int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev) fsl_dev->drm->mode_config.max_height = 2047; fsl_dev->drm->mode_config.funcs = &fsl_dcu_drm_mode_config_funcs; - drm_kms_helper_poll_init(fsl_dev->drm); - fsl_dcu_drm_crtc_create(fsl_dev); - fsl_dcu_drm_encoder_create(fsl_dev, &fsl_dev->crtc); - fsl_dcu_drm_connector_create(fsl_dev, &fsl_dev->encoder); + ret = fsl_dcu_drm_crtc_create(fsl_dev); + if (ret) + return ret; + + ret = fsl_dcu_drm_encoder_create(fsl_dev, &fsl_dev->crtc); + if (ret) + goto fail_encoder; + + ret = fsl_dcu_drm_connector_create(fsl_dev, &fsl_dev->encoder); + if (ret) + goto fail_connector; + drm_mode_config_reset(fsl_dev->drm); + drm_kms_helper_poll_init(fsl_dev->drm); return 0; +fail_encoder: + fsl_dev->crtc.funcs->destroy(&fsl_dev->crtc); +fail_connector: + fsl_dev->encoder.funcs->destroy(&fsl_dev->encoder); + return ret; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c index 4b13cf919575..274558b3b32b 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c @@ -41,11 +41,17 @@ static int fsl_dcu_drm_plane_atomic_check(struct drm_plane *plane, { struct drm_framebuffer *fb = state->fb; + if (!state->fb || !state->crtc) + return 0; + switch (fb->pixel_format) { case DRM_FORMAT_RGB565: case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: case DRM_FORMAT_ARGB8888: - case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_XRGB1555: case DRM_FORMAT_ARGB1555: case DRM_FORMAT_YUV422: return 0; @@ -59,19 +65,15 @@ static void fsl_dcu_drm_plane_atomic_disable(struct drm_plane *plane, { struct fsl_dcu_drm_device *fsl_dev = plane->dev->dev_private; unsigned int value; - int index, ret; + int index; index = fsl_dcu_drm_plane_index(plane); if (index < 0) return; - ret = regmap_read(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), &value); - if (ret) - dev_err(fsl_dev->dev, "read DCU_INT_MASK failed\n"); + regmap_read(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), &value); value &= ~DCU_LAYER_EN; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), value); - if (ret) - dev_err(fsl_dev->dev, "set DCU register failed\n"); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), value); } static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane, @@ -82,8 +84,8 @@ static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *state = plane->state; struct drm_framebuffer *fb = plane->state->fb; struct drm_gem_cma_object *gem; - unsigned int alpha, bpp; - int index, ret; + unsigned int alpha = DCU_LAYER_AB_NONE, bpp; + int index; if (!fb) return; @@ -97,96 +99,74 @@ static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane, switch (fb->pixel_format) { case DRM_FORMAT_RGB565: bpp = FSL_DCU_RGB565; - alpha = 0xff; break; case DRM_FORMAT_RGB888: bpp = FSL_DCU_RGB888; - alpha = 0xff; break; case DRM_FORMAT_ARGB8888: + alpha = DCU_LAYER_AB_WHOLE_FRAME; + /* fall-through */ + case DRM_FORMAT_XRGB8888: bpp = FSL_DCU_ARGB8888; - alpha = 0xff; break; - case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_ARGB4444: + alpha = DCU_LAYER_AB_WHOLE_FRAME; + /* fall-through */ + case DRM_FORMAT_XRGB4444: bpp = FSL_DCU_ARGB4444; - alpha = 0xff; break; case DRM_FORMAT_ARGB1555: + alpha = DCU_LAYER_AB_WHOLE_FRAME; + /* fall-through */ + case DRM_FORMAT_XRGB1555: bpp = FSL_DCU_ARGB1555; - alpha = 0xff; break; case DRM_FORMAT_YUV422: bpp = FSL_DCU_YUV422; - alpha = 0xff; break; default: return; } - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 1), - DCU_LAYER_HEIGHT(state->crtc_h) | - DCU_LAYER_WIDTH(state->crtc_w)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 2), - DCU_LAYER_POSY(state->crtc_y) | - DCU_LAYER_POSX(state->crtc_x)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, - DCU_CTRLDESCLN(index, 3), gem->paddr); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), - DCU_LAYER_EN | - DCU_LAYER_TRANS(alpha) | - DCU_LAYER_BPP(bpp) | - DCU_LAYER_AB(0)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 5), - DCU_LAYER_CKMAX_R(0xFF) | - DCU_LAYER_CKMAX_G(0xFF) | - DCU_LAYER_CKMAX_B(0xFF)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 6), - DCU_LAYER_CKMIN_R(0) | - DCU_LAYER_CKMIN_G(0) | - DCU_LAYER_CKMIN_B(0)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 7), 0); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 8), - DCU_LAYER_FG_FCOLOR(0)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 9), - DCU_LAYER_BG_BCOLOR(0)); - if (ret) - goto set_failed; + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 1), + DCU_LAYER_HEIGHT(state->crtc_h) | + DCU_LAYER_WIDTH(state->crtc_w)); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 2), + DCU_LAYER_POSY(state->crtc_y) | + DCU_LAYER_POSX(state->crtc_x)); + regmap_write(fsl_dev->regmap, + DCU_CTRLDESCLN(index, 3), gem->paddr); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 4), + DCU_LAYER_EN | + DCU_LAYER_TRANS(0xff) | + DCU_LAYER_BPP(bpp) | + alpha); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 5), + DCU_LAYER_CKMAX_R(0xFF) | + DCU_LAYER_CKMAX_G(0xFF) | + DCU_LAYER_CKMAX_B(0xFF)); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 6), + DCU_LAYER_CKMIN_R(0) | + DCU_LAYER_CKMIN_G(0) | + DCU_LAYER_CKMIN_B(0)); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 7), 0); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 8), + DCU_LAYER_FG_FCOLOR(0)); + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 9), + DCU_LAYER_BG_BCOLOR(0)); + if (!strcmp(fsl_dev->soc->name, "ls1021a")) { - ret = regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 10), - DCU_LAYER_POST_SKIP(0) | - DCU_LAYER_PRE_SKIP(0)); - if (ret) - goto set_failed; + regmap_write(fsl_dev->regmap, DCU_CTRLDESCLN(index, 10), + DCU_LAYER_POST_SKIP(0) | + DCU_LAYER_PRE_SKIP(0)); } - ret = regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, - DCU_MODE_DCU_MODE_MASK, - DCU_MODE_DCU_MODE(DCU_MODE_NORMAL)); - if (ret) - goto set_failed; - ret = regmap_write(fsl_dev->regmap, - DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG); - if (ret) - goto set_failed; - return; + regmap_update_bits(fsl_dev->regmap, DCU_DCU_MODE, + DCU_MODE_DCU_MODE_MASK, + DCU_MODE_DCU_MODE(DCU_MODE_NORMAL)); + regmap_write(fsl_dev->regmap, + DCU_UPDATE_MODE, DCU_UPDATE_MODE_READREG); -set_failed: - dev_err(fsl_dev->dev, "set DCU register failed\n"); + return; } static void @@ -213,6 +193,7 @@ static const struct drm_plane_helper_funcs fsl_dcu_drm_plane_helper_funcs = { static void fsl_dcu_drm_plane_destroy(struct drm_plane *plane) { drm_plane_cleanup(plane); + kfree(plane); } static const struct drm_plane_funcs fsl_dcu_drm_plane_funcs = { @@ -227,8 +208,11 @@ static const struct drm_plane_funcs fsl_dcu_drm_plane_funcs = { static const u32 fsl_dcu_drm_plane_formats[] = { DRM_FORMAT_RGB565, DRM_FORMAT_RGB888, + DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, + DRM_FORMAT_XRGB4444, DRM_FORMAT_ARGB4444, + DRM_FORMAT_XRGB1555, DRM_FORMAT_ARGB1555, DRM_FORMAT_YUV422, }; diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index 051eab33e4c7..20a5d0455e19 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -2,9 +2,7 @@ config DRM_I915 tristate "Intel 8xx/9xx/G3x/G4x/HD Graphics" depends on DRM depends on X86 && PCI - depends on (AGP || AGP=n) select INTEL_GTT - select AGP_INTEL if AGP select INTERVAL_TREE # we need shmfs for the swappable backing store, and in particular # the shmem_readpage() which depends upon tmpfs @@ -47,3 +45,14 @@ config DRM_I915_PRELIMINARY_HW_SUPPORT option changes the default for that module option. If in doubt, say "N". + +config DRM_I915_USERPTR + bool "Always enable userptr support" + depends on DRM_I915 + select MMU_NOTIFIER + default y + help + This option selects CONFIG_MMU_NOTIFIER if it isn't already + selected to enabled full userptr support. + + If in doubt, say "Y". diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index c5db23511184..a0f1bd711b53 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -117,9 +117,8 @@ static u64 i915_gem_obj_total_ggtt_size(struct drm_i915_gem_object *obj) u64 size = 0; struct i915_vma *vma; - list_for_each_entry(vma, &obj->vma_list, vma_link) { - if (i915_is_ggtt(vma->vm) && - drm_mm_node_allocated(&vma->node)) + list_for_each_entry(vma, &obj->vma_list, obj_link) { + if (vma->is_ggtt && drm_mm_node_allocated(&vma->node)) size += vma->node.size; } @@ -155,7 +154,7 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) obj->madv == I915_MADV_DONTNEED ? " purgeable" : ""); if (obj->base.name) seq_printf(m, " (name: %d)", obj->base.name); - list_for_each_entry(vma, &obj->vma_list, vma_link) { + list_for_each_entry(vma, &obj->vma_list, obj_link) { if (vma->pin_count > 0) pin_count++; } @@ -164,14 +163,13 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) seq_printf(m, " (display)"); if (obj->fence_reg != I915_FENCE_REG_NONE) seq_printf(m, " (fence: %d)", obj->fence_reg); - list_for_each_entry(vma, &obj->vma_list, vma_link) { + list_for_each_entry(vma, &obj->vma_list, obj_link) { seq_printf(m, " (%sgtt offset: %08llx, size: %08llx", - i915_is_ggtt(vma->vm) ? "g" : "pp", + vma->is_ggtt ? "g" : "pp", vma->node.start, vma->node.size); - if (i915_is_ggtt(vma->vm)) - seq_printf(m, ", type: %u)", vma->ggtt_view.type); - else - seq_puts(m, ")"); + if (vma->is_ggtt) + seq_printf(m, ", type: %u", vma->ggtt_view.type); + seq_puts(m, ")"); } if (obj->stolen) seq_printf(m, " (stolen: %08llx)", obj->stolen->start); @@ -230,7 +228,7 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data) } total_obj_size = total_gtt_size = count = 0; - list_for_each_entry(vma, head, mm_list) { + list_for_each_entry(vma, head, vm_link) { seq_printf(m, " "); describe_obj(m, vma->obj); seq_printf(m, "\n"); @@ -342,13 +340,13 @@ static int per_file_stats(int id, void *ptr, void *data) stats->shared += obj->base.size; if (USES_FULL_PPGTT(obj->base.dev)) { - list_for_each_entry(vma, &obj->vma_list, vma_link) { + list_for_each_entry(vma, &obj->vma_list, obj_link) { struct i915_hw_ppgtt *ppgtt; if (!drm_mm_node_allocated(&vma->node)) continue; - if (i915_is_ggtt(vma->vm)) { + if (vma->is_ggtt) { stats->global += obj->base.size; continue; } @@ -454,12 +452,12 @@ static int i915_gem_object_info(struct seq_file *m, void* data) count, mappable_count, size, mappable_size); size = count = mappable_size = mappable_count = 0; - count_vmas(&vm->active_list, mm_list); + count_vmas(&vm->active_list, vm_link); seq_printf(m, " %u [%u] active objects, %llu [%llu] bytes\n", count, mappable_count, size, mappable_size); size = count = mappable_size = mappable_count = 0; - count_vmas(&vm->inactive_list, mm_list); + count_vmas(&vm->inactive_list, vm_link); seq_printf(m, " %u [%u] inactive objects, %llu [%llu] bytes\n", count, mappable_count, size, mappable_size); @@ -825,8 +823,11 @@ static int i915_interrupt_info(struct seq_file *m, void *data) } for_each_pipe(dev_priv, pipe) { - if (!intel_display_power_is_enabled(dev_priv, - POWER_DOMAIN_PIPE(pipe))) { + enum intel_display_power_domain power_domain; + + power_domain = POWER_DOMAIN_PIPE(pipe); + if (!intel_display_power_get_if_enabled(dev_priv, + power_domain)) { seq_printf(m, "Pipe %c power disabled\n", pipe_name(pipe)); continue; @@ -840,6 +841,8 @@ static int i915_interrupt_info(struct seq_file *m, void *data) seq_printf(m, "Pipe %c IER:\t%08x\n", pipe_name(pipe), I915_READ(GEN8_DE_PIPE_IER(pipe))); + + intel_display_power_put(dev_priv, power_domain); } seq_printf(m, "Display Engine port interrupt mask:\t%08x\n", @@ -2463,9 +2466,9 @@ static void i915_guc_client_info(struct seq_file *m, for_each_ring(ring, dev_priv, i) { seq_printf(m, "\tSubmissions: %llu %s\n", - client->submissions[i], + client->submissions[ring->guc_id], ring->name); - tot += client->submissions[i]; + tot += client->submissions[ring->guc_id]; } seq_printf(m, "\tTotal: %llu\n", tot); } @@ -2502,10 +2505,10 @@ static int i915_guc_info(struct seq_file *m, void *data) seq_printf(m, "\nGuC submissions:\n"); for_each_ring(ring, dev_priv, i) { - seq_printf(m, "\t%-24s: %10llu, last seqno 0x%08x %9d\n", - ring->name, guc.submissions[i], - guc.last_seqno[i], guc.last_seqno[i]); - total += guc.submissions[i]; + seq_printf(m, "\t%-24s: %10llu, last seqno 0x%08x\n", + ring->name, guc.submissions[ring->guc_id], + guc.last_seqno[ring->guc_id]); + total += guc.submissions[ring->guc_id]; } seq_printf(m, "\t%s: %llu\n", "Total", total); @@ -2583,6 +2586,10 @@ static int i915_edp_psr_status(struct seq_file *m, void *data) enabled = true; } } + + seq_printf(m, "Main link in standby mode: %s\n", + yesno(dev_priv->psr.link_standby)); + seq_printf(m, "HW Enabled & Active bit: %s", yesno(enabled)); if (!HAS_DDI(dev)) @@ -3221,9 +3228,11 @@ static int i915_wa_registers(struct seq_file *m, void *unused) { int i; int ret; + struct intel_engine_cs *ring; struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct i915_workarounds *workarounds = &dev_priv->workarounds; ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) @@ -3231,15 +3240,18 @@ static int i915_wa_registers(struct seq_file *m, void *unused) intel_runtime_pm_get(dev_priv); - seq_printf(m, "Workarounds applied: %d\n", dev_priv->workarounds.count); - for (i = 0; i < dev_priv->workarounds.count; ++i) { + seq_printf(m, "Workarounds applied: %d\n", workarounds->count); + for_each_ring(ring, dev_priv, i) + seq_printf(m, "HW whitelist count for %s: %d\n", + ring->name, workarounds->hw_whitelist_count[i]); + for (i = 0; i < workarounds->count; ++i) { i915_reg_t addr; u32 mask, value, read; bool ok; - addr = dev_priv->workarounds.reg[i].addr; - mask = dev_priv->workarounds.reg[i].mask; - value = dev_priv->workarounds.reg[i].value; + addr = workarounds->reg[i].addr; + mask = workarounds->reg[i].mask; + value = workarounds->reg[i].value; read = I915_READ(addr); ok = (value & mask) == (read & mask); seq_printf(m, "0x%X: 0x%08X, mask: 0x%08X, read: 0x%08x, status: %s\n", @@ -3995,6 +4007,7 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe, struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe]; struct intel_crtc *crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe)); + enum intel_display_power_domain power_domain; u32 val = 0; /* shut up gcc */ int ret; @@ -4005,7 +4018,8 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe, if (pipe_crc->source && source) return -EINVAL; - if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PIPE(pipe))) { + power_domain = POWER_DOMAIN_PIPE(pipe); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) { DRM_DEBUG_KMS("Trying to capture CRC while pipe is off\n"); return -EIO; } @@ -4022,7 +4036,7 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe, ret = ivb_pipe_crc_ctl_reg(dev, pipe, &source, &val); if (ret != 0) - return ret; + goto out; /* none -> real source transition */ if (source) { @@ -4034,8 +4048,10 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe, entries = kcalloc(INTEL_PIPE_CRC_ENTRIES_NR, sizeof(pipe_crc->entries[0]), GFP_KERNEL); - if (!entries) - return -ENOMEM; + if (!entries) { + ret = -ENOMEM; + goto out; + } /* * When IPS gets enabled, the pipe CRC changes. Since IPS gets @@ -4091,7 +4107,12 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe, hsw_enable_ips(crtc); } - return 0; + ret = 0; + +out: + intel_display_power_put(dev_priv, power_domain); + + return ret; } /* diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 1c3d2544fec4..1c6d227aae7c 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -391,20 +391,13 @@ static int i915_load_modeset_init(struct drm_device *dev) if (ret) goto cleanup_vga_client; - /* Initialise stolen first so that we may reserve preallocated - * objects for the BIOS to KMS transition. - */ - ret = i915_gem_init_stolen(dev); - if (ret) - goto cleanup_vga_switcheroo; - intel_power_domains_init_hw(dev_priv, false); intel_csr_ucode_init(dev_priv); ret = intel_irq_install(dev_priv); if (ret) - goto cleanup_gem_stolen; + goto cleanup_csr; intel_setup_gmbus(dev); @@ -458,9 +451,8 @@ cleanup_irq: intel_guc_ucode_fini(dev); drm_irq_uninstall(dev); intel_teardown_gmbus(dev); -cleanup_gem_stolen: - i915_gem_cleanup_stolen(dev); -cleanup_vga_switcheroo: +cleanup_csr: + intel_csr_ucode_fini(dev_priv); vga_switcheroo_unregister_client(dev->pdev); cleanup_vga_client: vga_client_register(dev->pdev, NULL, NULL, NULL); @@ -816,7 +808,41 @@ static void intel_device_info_runtime_init(struct drm_device *dev) !(sfuse_strap & SFUSE_STRAP_FUSE_LOCK))) { DRM_INFO("Display fused off, disabling\n"); info->num_pipes = 0; + } else if (fuse_strap & IVB_PIPE_C_DISABLE) { + DRM_INFO("PipeC fused off\n"); + info->num_pipes -= 1; } + } else if (info->num_pipes > 0 && INTEL_INFO(dev)->gen == 9) { + u32 dfsm = I915_READ(SKL_DFSM); + u8 disabled_mask = 0; + bool invalid; + int num_bits; + + if (dfsm & SKL_DFSM_PIPE_A_DISABLE) + disabled_mask |= BIT(PIPE_A); + if (dfsm & SKL_DFSM_PIPE_B_DISABLE) + disabled_mask |= BIT(PIPE_B); + if (dfsm & SKL_DFSM_PIPE_C_DISABLE) + disabled_mask |= BIT(PIPE_C); + + num_bits = hweight8(disabled_mask); + + switch (disabled_mask) { + case BIT(PIPE_A): + case BIT(PIPE_B): + case BIT(PIPE_A) | BIT(PIPE_B): + case BIT(PIPE_A) | BIT(PIPE_C): + invalid = true; + break; + default: + invalid = false; + } + + if (num_bits > info->num_pipes || invalid) + DRM_ERROR("invalid pipe fuse configuration: 0x%x\n", + disabled_mask); + else + info->num_pipes -= num_bits; } /* Initialize slice/subslice/EU info */ @@ -855,6 +881,94 @@ static void intel_init_dpio(struct drm_i915_private *dev_priv) } } +static int i915_workqueues_init(struct drm_i915_private *dev_priv) +{ + /* + * The i915 workqueue is primarily used for batched retirement of + * requests (and thus managing bo) once the task has been completed + * by the GPU. i915_gem_retire_requests() is called directly when we + * need high-priority retirement, such as waiting for an explicit + * bo. + * + * It is also used for periodic low-priority events, such as + * idle-timers and recording error state. + * + * All tasks on the workqueue are expected to acquire the dev mutex + * so there is no point in running more than one instance of the + * workqueue at any time. Use an ordered one. + */ + dev_priv->wq = alloc_ordered_workqueue("i915", 0); + if (dev_priv->wq == NULL) + goto out_err; + + dev_priv->hotplug.dp_wq = alloc_ordered_workqueue("i915-dp", 0); + if (dev_priv->hotplug.dp_wq == NULL) + goto out_free_wq; + + dev_priv->gpu_error.hangcheck_wq = + alloc_ordered_workqueue("i915-hangcheck", 0); + if (dev_priv->gpu_error.hangcheck_wq == NULL) + goto out_free_dp_wq; + + return 0; + +out_free_dp_wq: + destroy_workqueue(dev_priv->hotplug.dp_wq); +out_free_wq: + destroy_workqueue(dev_priv->wq); +out_err: + DRM_ERROR("Failed to allocate workqueues.\n"); + + return -ENOMEM; +} + +static void i915_workqueues_cleanup(struct drm_i915_private *dev_priv) +{ + destroy_workqueue(dev_priv->gpu_error.hangcheck_wq); + destroy_workqueue(dev_priv->hotplug.dp_wq); + destroy_workqueue(dev_priv->wq); +} + +static int i915_mmio_setup(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + int mmio_bar; + int mmio_size; + + mmio_bar = IS_GEN2(dev) ? 1 : 0; + /* + * Before gen4, the registers and the GTT are behind different BARs. + * However, from gen4 onwards, the registers and the GTT are shared + * in the same BAR, so we want to restrict this ioremap from + * clobbering the GTT which we want ioremap_wc instead. Fortunately, + * the register BAR remains the same size for all the earlier + * generations up to Ironlake. + */ + if (INTEL_INFO(dev)->gen < 5) + mmio_size = 512 * 1024; + else + mmio_size = 2 * 1024 * 1024; + dev_priv->regs = pci_iomap(dev->pdev, mmio_bar, mmio_size); + if (dev_priv->regs == NULL) { + DRM_ERROR("failed to map registers\n"); + + return -EIO; + } + + /* Try to make sure MCHBAR is enabled before poking at it */ + intel_setup_mchbar(dev); + + return 0; +} + +static void i915_mmio_cleanup(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + + intel_teardown_mchbar(dev); + pci_iounmap(dev->pdev, dev_priv->regs); +} + /** * i915_driver_load - setup chip and create an initial config * @dev: DRM device @@ -870,7 +984,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) { struct drm_i915_private *dev_priv; struct intel_device_info *info, *device_info; - int ret = 0, mmio_bar, mmio_size; + int ret = 0; uint32_t aperture_size; info = (struct intel_device_info *) flags; @@ -897,6 +1011,10 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) mutex_init(&dev_priv->modeset_restore_lock); mutex_init(&dev_priv->av_mutex); + ret = i915_workqueues_init(dev_priv); + if (ret < 0) + goto out_free_priv; + intel_pm_setup(dev); intel_runtime_pm_get(dev_priv); @@ -915,28 +1033,12 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) if (i915_get_bridge_dev(dev)) { ret = -EIO; - goto free_priv; + goto out_runtime_pm_put; } - mmio_bar = IS_GEN2(dev) ? 1 : 0; - /* Before gen4, the registers and the GTT are behind different BARs. - * However, from gen4 onwards, the registers and the GTT are shared - * in the same BAR, so we want to restrict this ioremap from - * clobbering the GTT which we want ioremap_wc instead. Fortunately, - * the register BAR remains the same size for all the earlier - * generations up to Ironlake. - */ - if (info->gen < 5) - mmio_size = 512*1024; - else - mmio_size = 2*1024*1024; - - dev_priv->regs = pci_iomap(dev->pdev, mmio_bar, mmio_size); - if (!dev_priv->regs) { - DRM_ERROR("failed to map registers\n"); - ret = -EIO; + ret = i915_mmio_setup(dev); + if (ret < 0) goto put_bridge; - } /* This must be called before any calls to HAS_PCH_* */ intel_detect_pch(dev); @@ -945,7 +1047,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) ret = i915_gem_gtt_init(dev); if (ret) - goto out_freecsr; + goto out_uncore_fini; /* WARNING: Apparently we must kick fbdev drivers before vgacon, * otherwise the vga fbdev driver falls over. */ @@ -991,49 +1093,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) dev_priv->gtt.mtrr = arch_phys_wc_add(dev_priv->gtt.mappable_base, aperture_size); - /* The i915 workqueue is primarily used for batched retirement of - * requests (and thus managing bo) once the task has been completed - * by the GPU. i915_gem_retire_requests() is called directly when we - * need high-priority retirement, such as waiting for an explicit - * bo. - * - * It is also used for periodic low-priority events, such as - * idle-timers and recording error state. - * - * All tasks on the workqueue are expected to acquire the dev mutex - * so there is no point in running more than one instance of the - * workqueue at any time. Use an ordered one. - */ - dev_priv->wq = alloc_ordered_workqueue("i915", 0); - if (dev_priv->wq == NULL) { - DRM_ERROR("Failed to create our workqueue.\n"); - ret = -ENOMEM; - goto out_mtrrfree; - } - - dev_priv->hotplug.dp_wq = alloc_ordered_workqueue("i915-dp", 0); - if (dev_priv->hotplug.dp_wq == NULL) { - DRM_ERROR("Failed to create our dp workqueue.\n"); - ret = -ENOMEM; - goto out_freewq; - } - - dev_priv->gpu_error.hangcheck_wq = - alloc_ordered_workqueue("i915-hangcheck", 0); - if (dev_priv->gpu_error.hangcheck_wq == NULL) { - DRM_ERROR("Failed to create our hangcheck workqueue.\n"); - ret = -ENOMEM; - goto out_freedpwq; - } - intel_irq_init(dev_priv); intel_uncore_sanitize(dev); - /* Try to make sure MCHBAR is enabled before poking at it */ - intel_setup_mchbar(dev); intel_opregion_setup(dev); - i915_gem_load(dev); + i915_gem_load_init(dev); + i915_gem_shrinker_init(dev_priv); /* On the 945G/GM, the chipset reports the MSI capability on the * integrated graphics even though the support isn't actually there @@ -1046,8 +1112,10 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) * be lost or delayed, but we use them anyways to avoid * stuck interrupts on some machines. */ - if (!IS_I945G(dev) && !IS_I945GM(dev)) - pci_enable_msi(dev->pdev); + if (!IS_I945G(dev) && !IS_I945GM(dev)) { + if (pci_enable_msi(dev->pdev) < 0) + DRM_DEBUG_DRIVER("can't enable MSI"); + } intel_device_info_runtime_init(dev); @@ -1097,38 +1165,29 @@ out_power_well: intel_power_domains_fini(dev_priv); drm_vblank_cleanup(dev); out_gem_unload: - WARN_ON(unregister_oom_notifier(&dev_priv->mm.oom_notifier)); - unregister_shrinker(&dev_priv->mm.shrinker); + i915_gem_shrinker_cleanup(dev_priv); if (dev->pdev->msi_enabled) pci_disable_msi(dev->pdev); intel_teardown_mchbar(dev); pm_qos_remove_request(&dev_priv->pm_qos); - destroy_workqueue(dev_priv->gpu_error.hangcheck_wq); -out_freedpwq: - destroy_workqueue(dev_priv->hotplug.dp_wq); -out_freewq: - destroy_workqueue(dev_priv->wq); -out_mtrrfree: arch_phys_wc_del(dev_priv->gtt.mtrr); io_mapping_free(dev_priv->gtt.mappable); out_gtt: i915_global_gtt_cleanup(dev); -out_freecsr: - intel_csr_ucode_fini(dev_priv); +out_uncore_fini: intel_uncore_fini(dev); - pci_iounmap(dev->pdev, dev_priv->regs); + i915_mmio_cleanup(dev); put_bridge: pci_dev_put(dev_priv->bridge_dev); -free_priv: - kmem_cache_destroy(dev_priv->requests); - kmem_cache_destroy(dev_priv->vmas); - kmem_cache_destroy(dev_priv->objects); - + i915_gem_load_cleanup(dev); +out_runtime_pm_put: intel_runtime_pm_put(dev_priv); - + i915_workqueues_cleanup(dev_priv); +out_free_priv: kfree(dev_priv); + return ret; } @@ -1153,8 +1212,7 @@ int i915_driver_unload(struct drm_device *dev) i915_teardown_sysfs(dev); - WARN_ON(unregister_oom_notifier(&dev_priv->mm.oom_notifier)); - unregister_shrinker(&dev_priv->mm.shrinker); + i915_gem_shrinker_cleanup(dev_priv); io_mapping_free(dev_priv->gtt.mappable); arch_phys_wc_del(dev_priv->gtt.mtrr); @@ -1182,6 +1240,8 @@ int i915_driver_unload(struct drm_device *dev) vga_switcheroo_unregister_client(dev->pdev); vga_client_register(dev->pdev, NULL, NULL, NULL); + intel_csr_ucode_fini(dev_priv); + /* Free error state after interrupts are fully disabled. */ cancel_delayed_work_sync(&dev_priv->gpu_error.hangcheck_work); i915_destroy_error_state(dev); @@ -1200,27 +1260,17 @@ int i915_driver_unload(struct drm_device *dev) i915_gem_context_fini(dev); mutex_unlock(&dev->struct_mutex); intel_fbc_cleanup_cfb(dev_priv); - i915_gem_cleanup_stolen(dev); - intel_csr_ucode_fini(dev_priv); - - intel_teardown_mchbar(dev); - - destroy_workqueue(dev_priv->hotplug.dp_wq); - destroy_workqueue(dev_priv->wq); - destroy_workqueue(dev_priv->gpu_error.hangcheck_wq); pm_qos_remove_request(&dev_priv->pm_qos); i915_global_gtt_cleanup(dev); intel_uncore_fini(dev); - if (dev_priv->regs != NULL) - pci_iounmap(dev->pdev, dev_priv->regs); + i915_mmio_cleanup(dev); - kmem_cache_destroy(dev_priv->requests); - kmem_cache_destroy(dev_priv->vmas); - kmem_cache_destroy(dev_priv->objects); + i915_gem_load_cleanup(dev); pci_dev_put(dev_priv->bridge_dev); + i915_workqueues_cleanup(dev_priv); kfree(dev_priv); return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 44912ecebc1a..20e82008b8b6 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -603,13 +603,7 @@ static int i915_drm_suspend(struct drm_device *dev) intel_suspend_gt_powersave(dev); - /* - * Disable CRTCs directly since we want to preserve sw state - * for _thaw. Also, power gate the CRTC power wells. - */ - drm_modeset_lock_all(dev); intel_display_suspend(dev); - drm_modeset_unlock_all(dev); intel_dp_mst_suspend(dev); @@ -764,9 +758,7 @@ static int i915_drm_resume(struct drm_device *dev) dev_priv->display.hpd_irq_setup(dev); spin_unlock_irq(&dev_priv->irq_lock); - drm_modeset_lock_all(dev); intel_display_resume(dev); - drm_modeset_unlock_all(dev); intel_dp_mst_resume(dev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index afb0beee9975..10480939159c 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -59,7 +59,7 @@ #define DRIVER_NAME "i915" #define DRIVER_DESC "Intel Graphics" -#define DRIVER_DATE "20160124" +#define DRIVER_DATE "20160229" #undef WARN_ON /* Many gcc seem to no see through this and fall over :( */ @@ -261,6 +261,9 @@ struct i915_hotplug { #define for_each_pipe(__dev_priv, __p) \ for ((__p) = 0; (__p) < INTEL_INFO(__dev_priv)->num_pipes; (__p)++) +#define for_each_pipe_masked(__dev_priv, __p, __mask) \ + for ((__p) = 0; (__p) < INTEL_INFO(__dev_priv)->num_pipes; (__p)++) \ + for_each_if ((__mask) & (1 << (__p))) #define for_each_plane(__dev_priv, __pipe, __p) \ for ((__p) = 0; \ (__p) < INTEL_INFO(__dev_priv)->num_sprites[(__pipe)] + 1; \ @@ -746,6 +749,7 @@ struct intel_csr { uint32_t mmio_count; i915_reg_t mmioaddr[8]; uint32_t mmiodata[8]; + uint32_t dc_state; }; #define DEV_INFO_FOR_EACH_FLAG(func, sep) \ @@ -900,16 +904,15 @@ enum fb_op_origin { ORIGIN_DIRTYFB, }; -struct i915_fbc { +struct intel_fbc { /* This is always the inner lock when overlapping with struct_mutex and * it's the outer lock when overlapping with stolen_lock. */ struct mutex lock; unsigned threshold; - unsigned int fb_id; unsigned int possible_framebuffer_bits; unsigned int busy_bits; + unsigned int visible_pipes_mask; struct intel_crtc *crtc; - int y; struct drm_mm_node compressed_fb; struct drm_mm_node *compressed_llb; @@ -919,18 +922,52 @@ struct i915_fbc { bool enabled; bool active; + struct intel_fbc_state_cache { + struct { + unsigned int mode_flags; + uint32_t hsw_bdw_pixel_rate; + } crtc; + + struct { + unsigned int rotation; + int src_w; + int src_h; + bool visible; + } plane; + + struct { + u64 ilk_ggtt_offset; + uint32_t pixel_format; + unsigned int stride; + int fence_reg; + unsigned int tiling_mode; + } fb; + } state_cache; + + struct intel_fbc_reg_params { + struct { + enum pipe pipe; + enum plane plane; + unsigned int fence_y_offset; + } crtc; + + struct { + u64 ggtt_offset; + uint32_t pixel_format; + unsigned int stride; + int fence_reg; + } fb; + + int cfb_size; + } params; + struct intel_fbc_work { bool scheduled; + u32 scheduled_vblank; struct work_struct work; - struct drm_framebuffer *fb; - unsigned long enable_jiffies; } work; const char *no_fbc_reason; - - bool (*is_active)(struct drm_i915_private *dev_priv); - void (*activate)(struct intel_crtc *crtc); - void (*deactivate)(struct drm_i915_private *dev_priv); }; /** @@ -970,6 +1007,7 @@ struct i915_psr { unsigned busy_frontbuffer_bits; bool psr2_support; bool aux_frame_sync; + bool link_standby; }; enum intel_pch { @@ -1657,11 +1695,18 @@ struct i915_wa_reg { u32 mask; }; -#define I915_MAX_WA_REGS 16 +/* + * RING_MAX_NONPRIV_SLOTS is per-engine but at this point we are only + * allowing it for RCS as we don't foresee any requirement of having + * a whitelist for other engines. When it is really required for + * other engines then the limit need to be increased. + */ +#define I915_MAX_WA_REGS (16 + RING_MAX_NONPRIV_SLOTS) struct i915_workarounds { struct i915_wa_reg reg[I915_MAX_WA_REGS]; u32 count; + u32 hw_whitelist_count[I915_NUM_RINGS]; }; struct i915_virtual_gpu { @@ -1758,7 +1803,7 @@ struct drm_i915_private { u32 pipestat_irq_mask[I915_MAX_PIPES]; struct i915_hotplug hotplug; - struct i915_fbc fbc; + struct intel_fbc fbc; struct i915_drrs drrs; struct intel_opregion opregion; struct intel_vbt_data vbt; @@ -1807,6 +1852,7 @@ struct drm_i915_private { enum modeset_restore modeset_restore; struct mutex modeset_restore_lock; + struct drm_atomic_state *modeset_restore_state; struct list_head vm_list; /* Global list of all address spaces */ struct i915_gtt gtt; /* VM representing the global address space */ @@ -1993,6 +2039,9 @@ enum hdmi_force_audio { #define I915_GTT_OFFSET_NONE ((u32)-1) struct drm_i915_gem_object_ops { + unsigned int flags; +#define I915_GEM_OBJECT_HAS_STRUCT_PAGE 0x1 + /* Interface between the GEM object and its backing storage. * get_pages() is called once prior to the use of the associated set * of pages before to binding them into the GTT, and put_pages() is @@ -2008,6 +2057,7 @@ struct drm_i915_gem_object_ops { */ int (*get_pages)(struct drm_i915_gem_object *); void (*put_pages)(struct drm_i915_gem_object *); + int (*dmabuf_export)(struct drm_i915_gem_object *); void (*release)(struct drm_i915_gem_object *); }; @@ -2841,7 +2891,8 @@ int i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -void i915_gem_load(struct drm_device *dev); +void i915_gem_load_init(struct drm_device *dev); +void i915_gem_load_cleanup(struct drm_device *dev); void *i915_gem_object_alloc(struct drm_device *dev); void i915_gem_object_free(struct drm_i915_gem_object *obj); void i915_gem_object_init(struct drm_i915_gem_object *obj, @@ -3105,18 +3156,11 @@ bool i915_gem_obj_is_pinned(struct drm_i915_gem_object *obj); /* Some GGTT VM helpers */ #define i915_obj_to_ggtt(obj) \ (&((struct drm_i915_private *)(obj)->base.dev->dev_private)->gtt.base) -static inline bool i915_is_ggtt(struct i915_address_space *vm) -{ - struct i915_address_space *ggtt = - &((struct drm_i915_private *)(vm)->dev->dev_private)->gtt.base; - return vm == ggtt; -} static inline struct i915_hw_ppgtt * i915_vm_to_ppgtt(struct i915_address_space *vm) { WARN_ON(i915_is_ggtt(vm)); - return container_of(vm, struct i915_hw_ppgtt, base); } @@ -3254,6 +3298,7 @@ unsigned long i915_gem_shrink(struct drm_i915_private *dev_priv, #define I915_SHRINK_ACTIVE 0x8 unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv); void i915_gem_shrinker_init(struct drm_i915_private *dev_priv); +void i915_gem_shrinker_cleanup(struct drm_i915_private *dev_priv); /* i915_gem_tiling.c */ @@ -3424,16 +3469,14 @@ int sandybridge_pcode_write(struct drm_i915_private *dev_priv, u32 mbox, u32 val u32 vlv_punit_read(struct drm_i915_private *dev_priv, u32 addr); void vlv_punit_write(struct drm_i915_private *dev_priv, u32 addr, u32 val); u32 vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr); -u32 vlv_gpio_nc_read(struct drm_i915_private *dev_priv, u32 reg); -void vlv_gpio_nc_write(struct drm_i915_private *dev_priv, u32 reg, u32 val); +u32 vlv_iosf_sb_read(struct drm_i915_private *dev_priv, u8 port, u32 reg); +void vlv_iosf_sb_write(struct drm_i915_private *dev_priv, u8 port, u32 reg, u32 val); u32 vlv_cck_read(struct drm_i915_private *dev_priv, u32 reg); void vlv_cck_write(struct drm_i915_private *dev_priv, u32 reg, u32 val); u32 vlv_ccu_read(struct drm_i915_private *dev_priv, u32 reg); void vlv_ccu_write(struct drm_i915_private *dev_priv, u32 reg, u32 val); u32 vlv_bunit_read(struct drm_i915_private *dev_priv, u32 reg); void vlv_bunit_write(struct drm_i915_private *dev_priv, u32 reg, u32 val); -u32 vlv_gps_core_read(struct drm_i915_private *dev_priv, u32 reg); -void vlv_gps_core_write(struct drm_i915_private *dev_priv, u32 reg, u32 val); u32 vlv_dpio_read(struct drm_i915_private *dev_priv, enum pipe pipe, int reg); void vlv_dpio_write(struct drm_i915_private *dev_priv, enum pipe pipe, int reg, u32 val); u32 intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 371bbb28c471..3d31d3ac589e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -138,10 +138,10 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, pinned = 0; mutex_lock(&dev->struct_mutex); - list_for_each_entry(vma, &ggtt->base.active_list, mm_list) + list_for_each_entry(vma, &ggtt->base.active_list, vm_link) if (vma->pin_count) pinned += vma->node.size; - list_for_each_entry(vma, &ggtt->base.inactive_list, mm_list) + list_for_each_entry(vma, &ggtt->base.inactive_list, vm_link) if (vma->pin_count) pinned += vma->node.size; mutex_unlock(&dev->struct_mutex); @@ -272,7 +272,7 @@ drop_pages(struct drm_i915_gem_object *obj) int ret; drm_gem_object_reference(&obj->base); - list_for_each_entry_safe(vma, next, &obj->vma_list, vma_link) + list_for_each_entry_safe(vma, next, &obj->vma_list, obj_link) if (i915_vma_unbind(vma)) break; @@ -489,7 +489,7 @@ int i915_gem_obj_prepare_shmem_read(struct drm_i915_gem_object *obj, *needs_clflush = 0; - if (!obj->base.filp) + if (WARN_ON((obj->ops->flags & I915_GEM_OBJECT_HAS_STRUCT_PAGE) == 0)) return -EINVAL; if (!(obj->base.read_domains & I915_GEM_DOMAIN_CPU)) { @@ -2416,7 +2416,7 @@ void i915_vma_move_to_active(struct i915_vma *vma, list_move_tail(&obj->ring_list[ring->id], &ring->active_list); i915_gem_request_assign(&obj->last_read_req[ring->id], req); - list_move_tail(&vma->mm_list, &vma->vm->active_list); + list_move_tail(&vma->vm_link, &vma->vm->active_list); } static void @@ -2454,9 +2454,9 @@ i915_gem_object_retire__read(struct drm_i915_gem_object *obj, int ring) list_move_tail(&obj->global_list, &to_i915(obj->base.dev)->mm.bound_list); - list_for_each_entry(vma, &obj->vma_list, vma_link) { - if (!list_empty(&vma->mm_list)) - list_move_tail(&vma->mm_list, &vma->vm->inactive_list); + list_for_each_entry(vma, &obj->vma_list, obj_link) { + if (!list_empty(&vma->vm_link)) + list_move_tail(&vma->vm_link, &vma->vm->inactive_list); } i915_gem_request_assign(&obj->last_fenced_req, NULL); @@ -2680,7 +2680,7 @@ void i915_gem_request_free(struct kref *req_ref) if (ctx) { if (i915.enable_execlists && ctx != req->i915->kernel_context) - intel_lr_context_unpin(req); + intel_lr_context_unpin(ctx, req->ring); i915_gem_context_unreference(ctx); } @@ -2970,11 +2970,9 @@ i915_gem_retire_requests(struct drm_device *dev) i915_gem_retire_requests_ring(ring); idle &= list_empty(&ring->request_list); if (i915.enable_execlists) { - unsigned long flags; - - spin_lock_irqsave(&ring->execlist_lock, flags); + spin_lock_irq(&ring->execlist_lock); idle &= list_empty(&ring->execlist_queue); - spin_unlock_irqrestore(&ring->execlist_lock, flags); + spin_unlock_irq(&ring->execlist_lock); intel_execlists_retire_requests(ring); } @@ -3319,7 +3317,7 @@ static int __i915_vma_unbind(struct i915_vma *vma, bool wait) struct drm_i915_private *dev_priv = obj->base.dev->dev_private; int ret; - if (list_empty(&vma->vma_link)) + if (list_empty(&vma->obj_link)) return 0; if (!drm_mm_node_allocated(&vma->node)) { @@ -3338,8 +3336,7 @@ static int __i915_vma_unbind(struct i915_vma *vma, bool wait) return ret; } - if (i915_is_ggtt(vma->vm) && - vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL) { + if (vma->is_ggtt && vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL) { i915_gem_object_finish_gtt(obj); /* release the fence reg _after_ flushing */ @@ -3353,8 +3350,8 @@ static int __i915_vma_unbind(struct i915_vma *vma, bool wait) vma->vm->unbind_vma(vma); vma->bound = 0; - list_del_init(&vma->mm_list); - if (i915_is_ggtt(vma->vm)) { + list_del_init(&vma->vm_link); + if (vma->is_ggtt) { if (vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL) { obj->map_and_fenceable = false; } else if (vma->ggtt_view.pages) { @@ -3611,7 +3608,7 @@ search_free: goto err_remove_node; list_move_tail(&obj->global_list, &dev_priv->mm.bound_list); - list_add_tail(&vma->mm_list, &vm->inactive_list); + list_add_tail(&vma->vm_link, &vm->inactive_list); return vma; @@ -3776,7 +3773,7 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) /* And bump the LRU for this access */ vma = i915_gem_obj_to_ggtt(obj); if (vma && drm_mm_node_allocated(&vma->node) && !obj->active) - list_move_tail(&vma->mm_list, + list_move_tail(&vma->vm_link, &to_i915(obj->base.dev)->gtt.base.inactive_list); return 0; @@ -3811,7 +3808,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, * catch the issue of the CS prefetch crossing page boundaries and * reading an invalid PTE on older architectures. */ - list_for_each_entry_safe(vma, next, &obj->vma_list, vma_link) { + list_for_each_entry_safe(vma, next, &obj->vma_list, obj_link) { if (!drm_mm_node_allocated(&vma->node)) continue; @@ -3874,7 +3871,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, */ } - list_for_each_entry(vma, &obj->vma_list, vma_link) { + list_for_each_entry(vma, &obj->vma_list, obj_link) { if (!drm_mm_node_allocated(&vma->node)) continue; @@ -3884,7 +3881,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, } } - list_for_each_entry(vma, &obj->vma_list, vma_link) + list_for_each_entry(vma, &obj->vma_list, obj_link) vma->node.color = cache_level; obj->cache_level = cache_level; @@ -4465,6 +4462,7 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj, } static const struct drm_i915_gem_object_ops i915_gem_object_ops = { + .flags = I915_GEM_OBJECT_HAS_STRUCT_PAGE, .get_pages = i915_gem_object_get_pages_gtt, .put_pages = i915_gem_object_put_pages_gtt, }; @@ -4557,7 +4555,7 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) trace_i915_gem_object_destroy(obj); - list_for_each_entry_safe(vma, next, &obj->vma_list, vma_link) { + list_for_each_entry_safe(vma, next, &obj->vma_list, obj_link) { int ret; vma->pin_count = 0; @@ -4614,7 +4612,7 @@ struct i915_vma *i915_gem_obj_to_vma(struct drm_i915_gem_object *obj, struct i915_address_space *vm) { struct i915_vma *vma; - list_for_each_entry(vma, &obj->vma_list, vma_link) { + list_for_each_entry(vma, &obj->vma_list, obj_link) { if (vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL && vma->vm == vm) return vma; @@ -4631,7 +4629,7 @@ struct i915_vma *i915_gem_obj_to_ggtt_view(struct drm_i915_gem_object *obj, if (WARN_ONCE(!view, "no view specified")) return ERR_PTR(-EINVAL); - list_for_each_entry(vma, &obj->vma_list, vma_link) + list_for_each_entry(vma, &obj->vma_list, obj_link) if (vma->vm == ggtt && i915_ggtt_view_equal(&vma->ggtt_view, view)) return vma; @@ -4640,19 +4638,16 @@ struct i915_vma *i915_gem_obj_to_ggtt_view(struct drm_i915_gem_object *obj, void i915_gem_vma_destroy(struct i915_vma *vma) { - struct i915_address_space *vm = NULL; WARN_ON(vma->node.allocated); /* Keep the vma as a placeholder in the execbuffer reservation lists */ if (!list_empty(&vma->exec_list)) return; - vm = vma->vm; - - if (!i915_is_ggtt(vm)) - i915_ppgtt_put(i915_vm_to_ppgtt(vm)); + if (!vma->is_ggtt) + i915_ppgtt_put(i915_vm_to_ppgtt(vma->vm)); - list_del(&vma->vma_link); + list_del(&vma->obj_link); kmem_cache_free(to_i915(vma->obj->base.dev)->vmas, vma); } @@ -5034,7 +5029,7 @@ init_ring_lists(struct intel_engine_cs *ring) } void -i915_gem_load(struct drm_device *dev) +i915_gem_load_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int i; @@ -5100,11 +5095,18 @@ i915_gem_load(struct drm_device *dev) dev_priv->mm.interruptible = true; - i915_gem_shrinker_init(dev_priv); - mutex_init(&dev_priv->fb_tracking.lock); } +void i915_gem_load_cleanup(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + + kmem_cache_destroy(dev_priv->requests); + kmem_cache_destroy(dev_priv->vmas); + kmem_cache_destroy(dev_priv->objects); +} + void i915_gem_release(struct drm_device *dev, struct drm_file *file) { struct drm_i915_file_private *file_priv = file->driver_priv; @@ -5195,8 +5197,8 @@ u64 i915_gem_obj_offset(struct drm_i915_gem_object *o, WARN_ON(vm == &dev_priv->mm.aliasing_ppgtt->base); - list_for_each_entry(vma, &o->vma_list, vma_link) { - if (i915_is_ggtt(vma->vm) && + list_for_each_entry(vma, &o->vma_list, obj_link) { + if (vma->is_ggtt && vma->ggtt_view.type != I915_GGTT_VIEW_NORMAL) continue; if (vma->vm == vm) @@ -5214,7 +5216,7 @@ u64 i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o, struct i915_address_space *ggtt = i915_obj_to_ggtt(o); struct i915_vma *vma; - list_for_each_entry(vma, &o->vma_list, vma_link) + list_for_each_entry(vma, &o->vma_list, obj_link) if (vma->vm == ggtt && i915_ggtt_view_equal(&vma->ggtt_view, view)) return vma->node.start; @@ -5228,8 +5230,8 @@ bool i915_gem_obj_bound(struct drm_i915_gem_object *o, { struct i915_vma *vma; - list_for_each_entry(vma, &o->vma_list, vma_link) { - if (i915_is_ggtt(vma->vm) && + list_for_each_entry(vma, &o->vma_list, obj_link) { + if (vma->is_ggtt && vma->ggtt_view.type != I915_GGTT_VIEW_NORMAL) continue; if (vma->vm == vm && drm_mm_node_allocated(&vma->node)) @@ -5245,7 +5247,7 @@ bool i915_gem_obj_ggtt_bound_view(struct drm_i915_gem_object *o, struct i915_address_space *ggtt = i915_obj_to_ggtt(o); struct i915_vma *vma; - list_for_each_entry(vma, &o->vma_list, vma_link) + list_for_each_entry(vma, &o->vma_list, obj_link) if (vma->vm == ggtt && i915_ggtt_view_equal(&vma->ggtt_view, view) && drm_mm_node_allocated(&vma->node)) @@ -5258,7 +5260,7 @@ bool i915_gem_obj_bound_any(struct drm_i915_gem_object *o) { struct i915_vma *vma; - list_for_each_entry(vma, &o->vma_list, vma_link) + list_for_each_entry(vma, &o->vma_list, obj_link) if (drm_mm_node_allocated(&vma->node)) return true; @@ -5275,8 +5277,8 @@ unsigned long i915_gem_obj_size(struct drm_i915_gem_object *o, BUG_ON(list_empty(&o->vma_list)); - list_for_each_entry(vma, &o->vma_list, vma_link) { - if (i915_is_ggtt(vma->vm) && + list_for_each_entry(vma, &o->vma_list, obj_link) { + if (vma->is_ggtt && vma->ggtt_view.type != I915_GGTT_VIEW_NORMAL) continue; if (vma->vm == vm) @@ -5288,7 +5290,7 @@ unsigned long i915_gem_obj_size(struct drm_i915_gem_object *o, bool i915_gem_obj_is_pinned(struct drm_i915_gem_object *obj) { struct i915_vma *vma; - list_for_each_entry(vma, &obj->vma_list, vma_link) + list_for_each_entry(vma, &obj->vma_list, obj_link) if (vma->pin_count > 0) return true; @@ -5302,7 +5304,7 @@ i915_gem_object_get_dirty_page(struct drm_i915_gem_object *obj, int n) struct page *page; /* Only default objects have per-page dirty tracking */ - if (WARN_ON(obj->ops != &i915_gem_object_ops)) + if (WARN_ON((obj->ops->flags & I915_GEM_OBJECT_HAS_STRUCT_PAGE) == 0)) return NULL; page = i915_gem_object_get_page(obj, n); diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 6a4f64b03db6..5dd84e148bba 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -142,7 +142,7 @@ static void i915_gem_context_clean(struct intel_context *ctx) return; list_for_each_entry_safe(vma, next, &ppgtt->base.inactive_list, - mm_list) { + vm_link) { if (WARN_ON(__i915_vma_unbind_no_wait(vma))) break; } @@ -321,6 +321,18 @@ err_destroy: return ERR_PTR(ret); } +static void i915_gem_context_unpin(struct intel_context *ctx, + struct intel_engine_cs *engine) +{ + if (i915.enable_execlists) { + intel_lr_context_unpin(ctx, engine); + } else { + if (engine->id == RCS && ctx->legacy_hw_ctx.rcs_state) + i915_gem_object_ggtt_unpin(ctx->legacy_hw_ctx.rcs_state); + i915_gem_context_unreference(ctx); + } +} + void i915_gem_context_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -329,22 +341,15 @@ void i915_gem_context_reset(struct drm_device *dev) if (i915.enable_execlists) { struct intel_context *ctx; - list_for_each_entry(ctx, &dev_priv->context_list, link) { + list_for_each_entry(ctx, &dev_priv->context_list, link) intel_lr_context_reset(dev, ctx); - } - - return; } for (i = 0; i < I915_NUM_RINGS; i++) { struct intel_engine_cs *ring = &dev_priv->ring[i]; - struct intel_context *lctx = ring->last_context; - - if (lctx) { - if (lctx->legacy_hw_ctx.rcs_state && i == RCS) - i915_gem_object_ggtt_unpin(lctx->legacy_hw_ctx.rcs_state); - i915_gem_context_unreference(lctx); + if (ring->last_context) { + i915_gem_context_unpin(ring->last_context, ring); ring->last_context = NULL; } } @@ -417,13 +422,6 @@ void i915_gem_context_fini(struct drm_device *dev) * to offset the do_switch part, so that i915_gem_context_unreference() * can then free the base object correctly. */ WARN_ON(!dev_priv->ring[RCS].last_context); - if (dev_priv->ring[RCS].last_context == dctx) { - /* Fake switch to NULL context */ - WARN_ON(dctx->legacy_hw_ctx.rcs_state->active); - i915_gem_object_ggtt_unpin(dctx->legacy_hw_ctx.rcs_state); - i915_gem_context_unreference(dctx); - dev_priv->ring[RCS].last_context = NULL; - } i915_gem_object_ggtt_unpin(dctx->legacy_hw_ctx.rcs_state); } @@ -432,7 +430,7 @@ void i915_gem_context_fini(struct drm_device *dev) struct intel_engine_cs *ring = &dev_priv->ring[i]; if (ring->last_context) { - i915_gem_context_unreference(ring->last_context); + i915_gem_context_unpin(ring->last_context, ring); ring->last_context = NULL; } } @@ -857,6 +855,9 @@ int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, if (!contexts_enabled(dev)) return -ENODEV; + if (args->pad != 0) + return -EINVAL; + ret = i915_mutex_lock_interruptible(dev); if (ret) return ret; @@ -880,6 +881,9 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data, struct intel_context *ctx; int ret; + if (args->pad != 0) + return -EINVAL; + if (args->ctx_id == DEFAULT_CONTEXT_HANDLE) return -ENOENT; diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 07c6e4d320c9..ea1f8d1bd228 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -116,7 +116,7 @@ i915_gem_evict_something(struct drm_device *dev, struct i915_address_space *vm, search_again: /* First see if there is a large enough contiguous idle region... */ - list_for_each_entry(vma, &vm->inactive_list, mm_list) { + list_for_each_entry(vma, &vm->inactive_list, vm_link) { if (mark_free(vma, &unwind_list)) goto found; } @@ -125,7 +125,7 @@ search_again: goto none; /* Now merge in the soon-to-be-expired objects... */ - list_for_each_entry(vma, &vm->active_list, mm_list) { + list_for_each_entry(vma, &vm->active_list, vm_link) { if (mark_free(vma, &unwind_list)) goto found; } @@ -270,7 +270,7 @@ int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle) WARN_ON(!list_empty(&vm->active_list)); } - list_for_each_entry_safe(vma, next, &vm->inactive_list, mm_list) + list_for_each_entry_safe(vma, next, &vm->inactive_list, vm_link) if (vma->pin_count == 0) WARN_ON(i915_vma_unbind(vma)); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 2dc08ce1079a..1328bc5021b4 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -668,7 +668,7 @@ need_reloc_mappable(struct i915_vma *vma) if (entry->relocation_count == 0) return false; - if (!i915_is_ggtt(vma->vm)) + if (!vma->is_ggtt) return false; /* See also use_cpu_reloc() */ @@ -687,8 +687,7 @@ eb_vma_misplaced(struct i915_vma *vma) struct drm_i915_gem_exec_object2 *entry = vma->exec_entry; struct drm_i915_gem_object *obj = vma->obj; - WARN_ON(entry->flags & __EXEC_OBJECT_NEEDS_MAP && - !i915_is_ggtt(vma->vm)); + WARN_ON(entry->flags & __EXEC_OBJECT_NEEDS_MAP && !vma->is_ggtt); if (entry->alignment && vma->node.start & (entry->alignment - 1)) @@ -1401,6 +1400,7 @@ eb_select_ring(struct drm_i915_private *dev_priv, bsd_idx = gen8_dispatch_bsd_ring(dev_priv, file); } else if (bsd_idx >= I915_EXEC_BSD_RING1 && bsd_idx <= I915_EXEC_BSD_RING2) { + bsd_idx >>= I915_EXEC_BSD_SHIFT; bsd_idx--; } else { DRM_DEBUG("execbuf with unknown bsd ring: %u\n", @@ -1654,7 +1654,7 @@ err: * must be freed again. If it was submitted then it is being tracked * on the active request list and no clean up is required here. */ - if (ret && req) + if (ret && !IS_ERR_OR_NULL(req)) i915_gem_request_cancel(req); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 7377b6725c33..49e4f26b79d8 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -2132,6 +2132,25 @@ static void i915_address_space_init(struct i915_address_space *vm, list_add_tail(&vm->global_link, &dev_priv->vm_list); } +static void gtt_write_workarounds(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + /* This function is for gtt related workarounds. This function is + * called on driver load and after a GPU reset, so you can place + * workarounds here even if they get overwritten by GPU reset. + */ + /* WaIncreaseDefaultTLBEntries:chv,bdw,skl,bxt */ + if (IS_BROADWELL(dev)) + I915_WRITE(GEN8_L3_LRA_1_GPGPU, GEN8_L3_LRA_1_GPGPU_DEFAULT_VALUE_BDW); + else if (IS_CHERRYVIEW(dev)) + I915_WRITE(GEN8_L3_LRA_1_GPGPU, GEN8_L3_LRA_1_GPGPU_DEFAULT_VALUE_CHV); + else if (IS_SKYLAKE(dev)) + I915_WRITE(GEN8_L3_LRA_1_GPGPU, GEN9_L3_LRA_1_GPGPU_DEFAULT_VALUE_SKL); + else if (IS_BROXTON(dev)) + I915_WRITE(GEN8_L3_LRA_1_GPGPU, GEN9_L3_LRA_1_GPGPU_DEFAULT_VALUE_BXT); +} + int i915_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -2148,6 +2167,8 @@ int i915_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt) int i915_ppgtt_init_hw(struct drm_device *dev) { + gtt_write_workarounds(dev); + /* In the case of execlists, PPGTT is enabled by the context descriptor * and the PDPs are contained within the context itself. We don't * need to do anything here. */ @@ -2737,7 +2758,7 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev, } vma->bound |= GLOBAL_BIND; __i915_vma_set_map_and_fenceable(vma); - list_add_tail(&vma->mm_list, &ggtt_vm->inactive_list); + list_add_tail(&vma->vm_link, &ggtt_vm->inactive_list); } /* Clear any non-preallocated blocks */ @@ -2809,6 +2830,8 @@ void i915_global_gtt_cleanup(struct drm_device *dev) ppgtt->base.cleanup(&ppgtt->base); } + i915_gem_cleanup_stolen(dev); + if (drm_mm_initialized(&vm->mm)) { if (intel_vgpu_active(dev)) intel_vgt_deballoon(); @@ -3175,12 +3198,21 @@ int i915_gem_gtt_init(struct drm_device *dev) } gtt->base.dev = dev; + gtt->base.is_ggtt = true; ret = gtt->gtt_probe(dev, >t->base.total, >t->stolen_size, >t->mappable_base, >t->mappable_end); if (ret) return ret; + /* + * Initialise stolen early so that we may reserve preallocated + * objects for the BIOS to KMS transition. + */ + ret = i915_gem_init_stolen(dev); + if (ret) + goto out_gtt_cleanup; + /* GMADR is the PCI mmio aperture into the global GTT. */ DRM_INFO("Memory usable by graphics device = %lluM\n", gtt->base.total >> 20); @@ -3200,6 +3232,11 @@ int i915_gem_gtt_init(struct drm_device *dev) DRM_DEBUG_DRIVER("ppgtt mode: %i\n", i915.enable_ppgtt); return 0; + +out_gtt_cleanup: + gtt->base.cleanup(&dev_priv->gtt.base); + + return ret; } void i915_gem_restore_gtt_mappings(struct drm_device *dev) @@ -3222,7 +3259,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) vm = &dev_priv->gtt.base; list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { flush = false; - list_for_each_entry(vma, &obj->vma_list, vma_link) { + list_for_each_entry(vma, &obj->vma_list, obj_link) { if (vma->vm != vm) continue; @@ -3278,19 +3315,20 @@ __i915_gem_vma_create(struct drm_i915_gem_object *obj, if (vma == NULL) return ERR_PTR(-ENOMEM); - INIT_LIST_HEAD(&vma->vma_link); - INIT_LIST_HEAD(&vma->mm_list); + INIT_LIST_HEAD(&vma->vm_link); + INIT_LIST_HEAD(&vma->obj_link); INIT_LIST_HEAD(&vma->exec_list); vma->vm = vm; vma->obj = obj; + vma->is_ggtt = i915_is_ggtt(vm); if (i915_is_ggtt(vm)) vma->ggtt_view = *ggtt_view; - - list_add_tail(&vma->vma_link, &obj->vma_list); - if (!i915_is_ggtt(vm)) + else i915_ppgtt_get(i915_vm_to_ppgtt(vm)); + list_add_tail(&vma->obj_link, &obj->vma_list); + return vma; } @@ -3333,6 +3371,7 @@ i915_gem_obj_lookup_or_create_ggtt_vma(struct drm_i915_gem_object *obj, static struct scatterlist * rotate_pages(const dma_addr_t *in, unsigned int offset, unsigned int width, unsigned int height, + unsigned int stride, struct sg_table *st, struct scatterlist *sg) { unsigned int column, row; @@ -3344,7 +3383,7 @@ rotate_pages(const dma_addr_t *in, unsigned int offset, } for (column = 0; column < width; column++) { - src_idx = width * (height - 1) + column; + src_idx = stride * (height - 1) + column; for (row = 0; row < height; row++) { st->nents++; /* We don't need the pages, but need to initialize @@ -3355,7 +3394,7 @@ rotate_pages(const dma_addr_t *in, unsigned int offset, sg_dma_address(sg) = in[offset + src_idx]; sg_dma_len(sg) = PAGE_SIZE; sg = sg_next(sg); - src_idx -= width; + src_idx -= stride; } } @@ -3363,10 +3402,9 @@ rotate_pages(const dma_addr_t *in, unsigned int offset, } static struct sg_table * -intel_rotate_fb_obj_pages(struct i915_ggtt_view *ggtt_view, +intel_rotate_fb_obj_pages(struct intel_rotation_info *rot_info, struct drm_i915_gem_object *obj) { - struct intel_rotation_info *rot_info = &ggtt_view->params.rotation_info; unsigned int size_pages = rot_info->size >> PAGE_SHIFT; unsigned int size_pages_uv; struct sg_page_iter sg_iter; @@ -3408,6 +3446,7 @@ intel_rotate_fb_obj_pages(struct i915_ggtt_view *ggtt_view, /* Rotate the pages. */ sg = rotate_pages(page_addr_list, 0, rot_info->width_pages, rot_info->height_pages, + rot_info->width_pages, st, NULL); /* Append the UV plane if NV12. */ @@ -3423,6 +3462,7 @@ intel_rotate_fb_obj_pages(struct i915_ggtt_view *ggtt_view, rotate_pages(page_addr_list, uv_start_page, rot_info->width_pages_uv, rot_info->height_pages_uv, + rot_info->width_pages_uv, st, sg); } @@ -3504,7 +3544,7 @@ i915_get_ggtt_vma_pages(struct i915_vma *vma) vma->ggtt_view.pages = vma->obj->pages; else if (vma->ggtt_view.type == I915_GGTT_VIEW_ROTATED) vma->ggtt_view.pages = - intel_rotate_fb_obj_pages(&vma->ggtt_view, vma->obj); + intel_rotate_fb_obj_pages(&vma->ggtt_view.params.rotated, vma->obj); else if (vma->ggtt_view.type == I915_GGTT_VIEW_PARTIAL) vma->ggtt_view.pages = intel_partial_pages(&vma->ggtt_view, vma->obj); @@ -3560,13 +3600,9 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level, return 0; if (vma->bound == 0 && vma->vm->allocate_va_range) { - trace_i915_va_alloc(vma->vm, - vma->node.start, - vma->node.size, - VM_TO_TRACE_NAME(vma->vm)); - /* XXX: i915_vma_pin() will fix this +- hack */ vma->pin_count++; + trace_i915_va_alloc(vma); ret = vma->vm->allocate_va_range(vma->vm, vma->node.start, vma->node.size); @@ -3598,7 +3634,7 @@ i915_ggtt_view_size(struct drm_i915_gem_object *obj, if (view->type == I915_GGTT_VIEW_NORMAL) { return obj->base.size; } else if (view->type == I915_GGTT_VIEW_ROTATED) { - return view->params.rotation_info.size; + return view->params.rotated.size; } else if (view->type == I915_GGTT_VIEW_PARTIAL) { return view->params.partial.size << PAGE_SHIFT; } else { diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.h b/drivers/gpu/drm/i915/i915_gem_gtt.h index e5737963ab79..8774f1ba46e7 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.h +++ b/drivers/gpu/drm/i915/i915_gem_gtt.h @@ -155,7 +155,7 @@ struct i915_ggtt_view { u64 offset; unsigned int size; } partial; - struct intel_rotation_info rotation_info; + struct intel_rotation_info rotated; } params; struct sg_table *pages; @@ -183,6 +183,7 @@ struct i915_vma { #define GLOBAL_BIND (1<<0) #define LOCAL_BIND (1<<1) unsigned int bound : 4; + bool is_ggtt : 1; /** * Support different GGTT views into the same object. @@ -194,9 +195,9 @@ struct i915_vma { struct i915_ggtt_view ggtt_view; /** This object's place on the active/inactive lists */ - struct list_head mm_list; + struct list_head vm_link; - struct list_head vma_link; /* Link in the object's VMA list */ + struct list_head obj_link; /* Link in the object's VMA list */ /** This vma's place in the batchbuffer or on the eviction list */ struct list_head exec_list; @@ -275,6 +276,8 @@ struct i915_address_space { u64 start; /* Start offset always 0 for dri2 */ u64 total; /* size addr space maps (ex. 2GB for ggtt) */ + bool is_ggtt; + struct i915_page_scratch *scratch_page; struct i915_page_table *scratch_pt; struct i915_page_directory *scratch_pd; @@ -330,6 +333,8 @@ struct i915_address_space { u32 flags); }; +#define i915_is_ggtt(V) ((V)->is_ggtt) + /* The Graphics Translation Table is the way in which GEN hardware translates a * Graphics Virtual Address into a Physical Address. In addition to the normal * collateral associated with any va->pa translations GEN hardware also has a @@ -342,6 +347,8 @@ struct i915_gtt { size_t stolen_size; /* Total size of stolen memory */ size_t stolen_usable_size; /* Total size minus BIOS reserved */ + size_t stolen_reserved_base; + size_t stolen_reserved_size; u64 mappable_end; /* End offset that we can CPU map */ struct io_mapping *mappable; /* Mapping to our CPU mappable region */ phys_addr_t mappable_base; /* PA of our GMADR */ @@ -416,7 +423,7 @@ static inline uint32_t i915_pte_index(uint64_t address, uint32_t pde_shift) static inline uint32_t i915_pte_count(uint64_t addr, size_t length, uint32_t pde_shift) { - const uint64_t mask = ~((1 << pde_shift) - 1); + const uint64_t mask = ~((1ULL << pde_shift) - 1); uint64_t end; WARN_ON(length == 0); diff --git a/drivers/gpu/drm/i915/i915_gem_shrinker.c b/drivers/gpu/drm/i915/i915_gem_shrinker.c index 16da9c1422cc..d3c473ffb90a 100644 --- a/drivers/gpu/drm/i915/i915_gem_shrinker.c +++ b/drivers/gpu/drm/i915/i915_gem_shrinker.c @@ -52,7 +52,7 @@ static int num_vma_bound(struct drm_i915_gem_object *obj) struct i915_vma *vma; int count = 0; - list_for_each_entry(vma, &obj->vma_list, vma_link) { + list_for_each_entry(vma, &obj->vma_list, obj_link) { if (drm_mm_node_allocated(&vma->node)) count++; if (vma->pin_count) @@ -176,7 +176,7 @@ i915_gem_shrink(struct drm_i915_private *dev_priv, /* For the unbound phase, this should be a no-op! */ list_for_each_entry_safe(vma, v, - &obj->vma_list, vma_link) + &obj->vma_list, obj_link) if (i915_vma_unbind(vma)) break; @@ -367,8 +367,20 @@ void i915_gem_shrinker_init(struct drm_i915_private *dev_priv) dev_priv->mm.shrinker.scan_objects = i915_gem_shrinker_scan; dev_priv->mm.shrinker.count_objects = i915_gem_shrinker_count; dev_priv->mm.shrinker.seeks = DEFAULT_SEEKS; - register_shrinker(&dev_priv->mm.shrinker); + WARN_ON(register_shrinker(&dev_priv->mm.shrinker)); dev_priv->mm.oom_notifier.notifier_call = i915_gem_shrinker_oom; - register_oom_notifier(&dev_priv->mm.oom_notifier); + WARN_ON(register_oom_notifier(&dev_priv->mm.oom_notifier)); +} + +/** + * i915_gem_shrinker_cleanup - Clean up i915 shrinker + * @dev_priv: i915 device + * + * This function unregisters the i915 shrinker and OOM handler. + */ +void i915_gem_shrinker_cleanup(struct drm_i915_private *dev_priv) +{ + WARN_ON(unregister_oom_notifier(&dev_priv->mm.oom_notifier)); + unregister_shrinker(&dev_priv->mm.shrinker); } diff --git a/drivers/gpu/drm/i915/i915_gem_stolen.c b/drivers/gpu/drm/i915/i915_gem_stolen.c index c384dc9c8a63..2e6e9fb6f80d 100644 --- a/drivers/gpu/drm/i915/i915_gem_stolen.c +++ b/drivers/gpu/drm/i915/i915_gem_stolen.c @@ -458,6 +458,9 @@ int i915_gem_init_stolen(struct drm_device *dev) return 0; } + dev_priv->gtt.stolen_reserved_base = reserved_base; + dev_priv->gtt.stolen_reserved_size = reserved_size; + /* It is possible for the reserved area to end before the end of stolen * memory, so just consider the start. */ reserved_total = stolen_top - reserved_base; @@ -635,6 +638,8 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, if (!drm_mm_initialized(&dev_priv->mm.stolen)) return NULL; + lockdep_assert_held(&dev->struct_mutex); + DRM_DEBUG_KMS("creating preallocated stolen object: stolen_offset=%x, gtt_offset=%x, size=%x\n", stolen_offset, gtt_offset, size); @@ -692,7 +697,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev, vma->bound |= GLOBAL_BIND; __i915_vma_set_map_and_fenceable(vma); - list_add_tail(&vma->mm_list, &ggtt->inactive_list); + list_add_tail(&vma->vm_link, &ggtt->inactive_list); } list_add_tail(&obj->global_list, &dev_priv->mm.bound_list); diff --git a/drivers/gpu/drm/i915/i915_gem_userptr.c b/drivers/gpu/drm/i915/i915_gem_userptr.c index 19fb0bddc1cd..4b09c840d493 100644 --- a/drivers/gpu/drm/i915/i915_gem_userptr.c +++ b/drivers/gpu/drm/i915/i915_gem_userptr.c @@ -49,21 +49,18 @@ struct i915_mmu_notifier { struct hlist_node node; struct mmu_notifier mn; struct rb_root objects; - struct list_head linear; - bool has_linear; }; struct i915_mmu_object { struct i915_mmu_notifier *mn; + struct drm_i915_gem_object *obj; struct interval_tree_node it; struct list_head link; - struct drm_i915_gem_object *obj; struct work_struct work; - bool active; - bool is_linear; + bool attached; }; -static void __cancel_userptr__worker(struct work_struct *work) +static void cancel_userptr(struct work_struct *work) { struct i915_mmu_object *mo = container_of(work, typeof(*mo), work); struct drm_i915_gem_object *obj = mo->obj; @@ -81,7 +78,7 @@ static void __cancel_userptr__worker(struct work_struct *work) was_interruptible = dev_priv->mm.interruptible; dev_priv->mm.interruptible = false; - list_for_each_entry_safe(vma, tmp, &obj->vma_list, vma_link) { + list_for_each_entry_safe(vma, tmp, &obj->vma_list, obj_link) { int ret = i915_vma_unbind(vma); WARN_ON(ret && ret != -EIO); } @@ -94,24 +91,22 @@ static void __cancel_userptr__worker(struct work_struct *work) mutex_unlock(&dev->struct_mutex); } -static unsigned long cancel_userptr(struct i915_mmu_object *mo) +static void add_object(struct i915_mmu_object *mo) { - unsigned long end = mo->obj->userptr.ptr + mo->obj->base.size; - - /* The mmu_object is released late when destroying the - * GEM object so it is entirely possible to gain a - * reference on an object in the process of being freed - * since our serialisation is via the spinlock and not - * the struct_mutex - and consequently use it after it - * is freed and then double free it. - */ - if (mo->active && kref_get_unless_zero(&mo->obj->base.refcount)) { - schedule_work(&mo->work); - /* only schedule one work packet to avoid the refleak */ - mo->active = false; - } + if (mo->attached) + return; + + interval_tree_insert(&mo->it, &mo->mn->objects); + mo->attached = true; +} - return end; +static void del_object(struct i915_mmu_object *mo) +{ + if (!mo->attached) + return; + + interval_tree_remove(&mo->it, &mo->mn->objects); + mo->attached = false; } static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn, @@ -122,28 +117,36 @@ static void i915_gem_userptr_mn_invalidate_range_start(struct mmu_notifier *_mn, struct i915_mmu_notifier *mn = container_of(_mn, struct i915_mmu_notifier, mn); struct i915_mmu_object *mo; + struct interval_tree_node *it; + LIST_HEAD(cancelled); + + if (RB_EMPTY_ROOT(&mn->objects)) + return; /* interval ranges are inclusive, but invalidate range is exclusive */ end--; spin_lock(&mn->lock); - if (mn->has_linear) { - list_for_each_entry(mo, &mn->linear, link) { - if (mo->it.last < start || mo->it.start > end) - continue; - - cancel_userptr(mo); - } - } else { - struct interval_tree_node *it; + it = interval_tree_iter_first(&mn->objects, start, end); + while (it) { + /* The mmu_object is released late when destroying the + * GEM object so it is entirely possible to gain a + * reference on an object in the process of being freed + * since our serialisation is via the spinlock and not + * the struct_mutex - and consequently use it after it + * is freed and then double free it. To prevent that + * use-after-free we only acquire a reference on the + * object if it is not in the process of being destroyed. + */ + mo = container_of(it, struct i915_mmu_object, it); + if (kref_get_unless_zero(&mo->obj->base.refcount)) + schedule_work(&mo->work); - it = interval_tree_iter_first(&mn->objects, start, end); - while (it) { - mo = container_of(it, struct i915_mmu_object, it); - start = cancel_userptr(mo); - it = interval_tree_iter_next(it, start, end); - } + list_add(&mo->link, &cancelled); + it = interval_tree_iter_next(it, start, end); } + list_for_each_entry(mo, &cancelled, link) + del_object(mo); spin_unlock(&mn->lock); } @@ -164,8 +167,6 @@ i915_mmu_notifier_create(struct mm_struct *mm) spin_lock_init(&mn->lock); mn->mn.ops = &i915_gem_userptr_notifier; mn->objects = RB_ROOT; - INIT_LIST_HEAD(&mn->linear); - mn->has_linear = false; /* Protected by mmap_sem (write-lock) */ ret = __mmu_notifier_register(&mn->mn, mm); @@ -177,85 +178,6 @@ i915_mmu_notifier_create(struct mm_struct *mm) return mn; } -static int -i915_mmu_notifier_add(struct drm_device *dev, - struct i915_mmu_notifier *mn, - struct i915_mmu_object *mo) -{ - struct interval_tree_node *it; - int ret = 0; - - /* By this point we have already done a lot of expensive setup that - * we do not want to repeat just because the caller (e.g. X) has a - * signal pending (and partly because of that expensive setup, X - * using an interrupt timer is likely to get stuck in an EINTR loop). - */ - mutex_lock(&dev->struct_mutex); - - /* Make sure we drop the final active reference (and thereby - * remove the objects from the interval tree) before we do - * the check for overlapping objects. - */ - i915_gem_retire_requests(dev); - - spin_lock(&mn->lock); - it = interval_tree_iter_first(&mn->objects, - mo->it.start, mo->it.last); - if (it) { - struct drm_i915_gem_object *obj; - - /* We only need to check the first object in the range as it - * either has cancelled gup work queued and we need to - * return back to the user to give time for the gup-workers - * to flush their object references upon which the object will - * be removed from the interval-tree, or the the range is - * still in use by another client and the overlap is invalid. - * - * If we do have an overlap, we cannot use the interval tree - * for fast range invalidation. - */ - - obj = container_of(it, struct i915_mmu_object, it)->obj; - if (!obj->userptr.workers) - mn->has_linear = mo->is_linear = true; - else - ret = -EAGAIN; - } else - interval_tree_insert(&mo->it, &mn->objects); - - if (ret == 0) - list_add(&mo->link, &mn->linear); - - spin_unlock(&mn->lock); - mutex_unlock(&dev->struct_mutex); - - return ret; -} - -static bool i915_mmu_notifier_has_linear(struct i915_mmu_notifier *mn) -{ - struct i915_mmu_object *mo; - - list_for_each_entry(mo, &mn->linear, link) - if (mo->is_linear) - return true; - - return false; -} - -static void -i915_mmu_notifier_del(struct i915_mmu_notifier *mn, - struct i915_mmu_object *mo) -{ - spin_lock(&mn->lock); - list_del(&mo->link); - if (mo->is_linear) - mn->has_linear = i915_mmu_notifier_has_linear(mn); - else - interval_tree_remove(&mo->it, &mn->objects); - spin_unlock(&mn->lock); -} - static void i915_gem_userptr_release__mmu_notifier(struct drm_i915_gem_object *obj) { @@ -265,7 +187,9 @@ i915_gem_userptr_release__mmu_notifier(struct drm_i915_gem_object *obj) if (mo == NULL) return; - i915_mmu_notifier_del(mo->mn, mo); + spin_lock(&mo->mn->lock); + del_object(mo); + spin_unlock(&mo->mn->lock); kfree(mo); obj->userptr.mmu_object = NULL; @@ -299,7 +223,6 @@ i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj, { struct i915_mmu_notifier *mn; struct i915_mmu_object *mo; - int ret; if (flags & I915_USERPTR_UNSYNCHRONIZED) return capable(CAP_SYS_ADMIN) ? 0 : -EPERM; @@ -316,16 +239,10 @@ i915_gem_userptr_init__mmu_notifier(struct drm_i915_gem_object *obj, return -ENOMEM; mo->mn = mn; - mo->it.start = obj->userptr.ptr; - mo->it.last = mo->it.start + obj->base.size - 1; mo->obj = obj; - INIT_WORK(&mo->work, __cancel_userptr__worker); - - ret = i915_mmu_notifier_add(obj->base.dev, mn, mo); - if (ret) { - kfree(mo); - return ret; - } + mo->it.start = obj->userptr.ptr; + mo->it.last = obj->userptr.ptr + obj->base.size - 1; + INIT_WORK(&mo->work, cancel_userptr); obj->userptr.mmu_object = mo; return 0; @@ -552,8 +469,10 @@ __i915_gem_userptr_set_active(struct drm_i915_gem_object *obj, /* In order to serialise get_pages with an outstanding * cancel_userptr, we must drop the struct_mutex and try again. */ - if (!value || !work_pending(&obj->userptr.mmu_object->work)) - obj->userptr.mmu_object->active = value; + if (!value) + del_object(obj->userptr.mmu_object); + else if (!work_pending(&obj->userptr.mmu_object->work)) + add_object(obj->userptr.mmu_object); else ret = -EAGAIN; spin_unlock(&obj->userptr.mmu_object->mn->lock); @@ -789,9 +708,10 @@ i915_gem_userptr_dmabuf_export(struct drm_i915_gem_object *obj) } static const struct drm_i915_gem_object_ops i915_gem_userptr_ops = { - .dmabuf_export = i915_gem_userptr_dmabuf_export, + .flags = I915_GEM_OBJECT_HAS_STRUCT_PAGE, .get_pages = i915_gem_userptr_get_pages, .put_pages = i915_gem_userptr_put_pages, + .dmabuf_export = i915_gem_userptr_dmabuf_export, .release = i915_gem_userptr_release, }; diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c index 7eeb24427785..831895b8cb75 100644 --- a/drivers/gpu/drm/i915/i915_gpu_error.c +++ b/drivers/gpu/drm/i915/i915_gpu_error.c @@ -365,6 +365,10 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m, err_printf(m, "Reset count: %u\n", error->reset_count); err_printf(m, "Suspend count: %u\n", error->suspend_count); err_printf(m, "PCI ID: 0x%04x\n", dev->pdev->device); + err_printf(m, "PCI Revision: 0x%02x\n", dev->pdev->revision); + err_printf(m, "PCI Subsystem: %04x:%04x\n", + dev->pdev->subsystem_vendor, + dev->pdev->subsystem_device); err_printf(m, "IOMMU enabled?: %d\n", error->iommu); if (HAS_CSR(dev)) { @@ -732,7 +736,7 @@ static u32 capture_active_bo(struct drm_i915_error_buffer *err, struct i915_vma *vma; int i = 0; - list_for_each_entry(vma, head, mm_list) { + list_for_each_entry(vma, head, vm_link) { capture_bo(err++, vma); if (++i == count) break; @@ -755,7 +759,7 @@ static u32 capture_pinned_bo(struct drm_i915_error_buffer *err, if (err == last) break; - list_for_each_entry(vma, &obj->vma_list, vma_link) + list_for_each_entry(vma, &obj->vma_list, obj_link) if (vma->vm == vm && vma->pin_count > 0) capture_bo(err++, vma); } @@ -1123,12 +1127,12 @@ static void i915_gem_capture_vm(struct drm_i915_private *dev_priv, int i; i = 0; - list_for_each_entry(vma, &vm->active_list, mm_list) + list_for_each_entry(vma, &vm->active_list, vm_link) i++; error->active_bo_count[ndx] = i; list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) { - list_for_each_entry(vma, &obj->vma_list, vma_link) + list_for_each_entry(vma, &obj->vma_list, obj_link) if (vma->vm == vm && vma->pin_count > 0) i++; } diff --git a/drivers/gpu/drm/i915/i915_guc_submission.c b/drivers/gpu/drm/i915/i915_guc_submission.c index 51ae5c1f806d..d7543efc8a5e 100644 --- a/drivers/gpu/drm/i915/i915_guc_submission.c +++ b/drivers/gpu/drm/i915/i915_guc_submission.c @@ -376,6 +376,8 @@ static void guc_init_proc_desc(struct intel_guc *guc, static void guc_init_ctx_desc(struct intel_guc *guc, struct i915_guc_client *client) { + struct drm_i915_private *dev_priv = guc_to_i915(guc); + struct intel_engine_cs *ring; struct intel_context *ctx = client->owner; struct guc_context_desc desc; struct sg_table *sg; @@ -388,10 +390,8 @@ static void guc_init_ctx_desc(struct intel_guc *guc, desc.priority = client->priority; desc.db_id = client->doorbell_id; - for (i = 0; i < I915_NUM_RINGS; i++) { - struct guc_execlist_context *lrc = &desc.lrc[i]; - struct intel_ringbuffer *ringbuf = ctx->engine[i].ringbuf; - struct intel_engine_cs *ring; + for_each_ring(ring, dev_priv, i) { + struct guc_execlist_context *lrc = &desc.lrc[ring->guc_id]; struct drm_i915_gem_object *obj; uint64_t ctx_desc; @@ -406,7 +406,6 @@ static void guc_init_ctx_desc(struct intel_guc *guc, if (!obj) break; /* XXX: continue? */ - ring = ringbuf->ring; ctx_desc = intel_lr_context_descriptor(ctx, ring); lrc->context_desc = (u32)ctx_desc; @@ -414,16 +413,16 @@ static void guc_init_ctx_desc(struct intel_guc *guc, lrc->ring_lcra = i915_gem_obj_ggtt_offset(obj) + LRC_STATE_PN * PAGE_SIZE; lrc->context_id = (client->ctx_index << GUC_ELC_CTXID_OFFSET) | - (ring->id << GUC_ELC_ENGINE_OFFSET); + (ring->guc_id << GUC_ELC_ENGINE_OFFSET); - obj = ringbuf->obj; + obj = ctx->engine[i].ringbuf->obj; lrc->ring_begin = i915_gem_obj_ggtt_offset(obj); lrc->ring_end = lrc->ring_begin + obj->base.size - 1; lrc->ring_next_free_location = lrc->ring_begin; lrc->ring_current_tail_pointer_value = 0; - desc.engines_used |= (1 << ring->id); + desc.engines_used |= (1 << ring->guc_id); } WARN_ON(desc.engines_used == 0); @@ -510,7 +509,6 @@ int i915_guc_wq_check_space(struct i915_guc_client *gc) static int guc_add_workqueue_item(struct i915_guc_client *gc, struct drm_i915_gem_request *rq) { - enum intel_ring_id ring_id = rq->ring->id; struct guc_wq_item *wqi; void *base; u32 tail, wq_len, wq_off, space; @@ -544,7 +542,7 @@ static int guc_add_workqueue_item(struct i915_guc_client *gc, wq_len = sizeof(struct guc_wq_item) / sizeof(u32) - 1; wqi->header = WQ_TYPE_INORDER | (wq_len << WQ_LEN_SHIFT) | - (ring_id << WQ_TARGET_SHIFT) | + (rq->ring->guc_id << WQ_TARGET_SHIFT) | WQ_NO_WCFLUSH_WAIT; /* The GuC wants only the low-order word of the context descriptor */ @@ -560,29 +558,6 @@ static int guc_add_workqueue_item(struct i915_guc_client *gc, return 0; } -#define CTX_RING_BUFFER_START 0x08 - -/* Update the ringbuffer pointer in a saved context image */ -static void lr_context_update(struct drm_i915_gem_request *rq) -{ - enum intel_ring_id ring_id = rq->ring->id; - struct drm_i915_gem_object *ctx_obj = rq->ctx->engine[ring_id].state; - struct drm_i915_gem_object *rb_obj = rq->ringbuf->obj; - struct page *page; - uint32_t *reg_state; - - BUG_ON(!ctx_obj); - WARN_ON(!i915_gem_obj_is_pinned(ctx_obj)); - WARN_ON(!i915_gem_obj_is_pinned(rb_obj)); - - page = i915_gem_object_get_dirty_page(ctx_obj, LRC_STATE_PN); - reg_state = kmap_atomic(page); - - reg_state[CTX_RING_BUFFER_START+1] = i915_gem_obj_ggtt_offset(rb_obj); - - kunmap_atomic(reg_state); -} - /** * i915_guc_submit() - Submit commands through GuC * @client: the guc client where commands will go through @@ -594,18 +569,14 @@ int i915_guc_submit(struct i915_guc_client *client, struct drm_i915_gem_request *rq) { struct intel_guc *guc = client->guc; - enum intel_ring_id ring_id = rq->ring->id; + unsigned int engine_id = rq->ring->guc_id; int q_ret, b_ret; - /* Need this because of the deferred pin ctx and ring */ - /* Shall we move this right after ring is pinned? */ - lr_context_update(rq); - q_ret = guc_add_workqueue_item(client, rq); if (q_ret == 0) b_ret = guc_ring_doorbell(client); - client->submissions[ring_id] += 1; + client->submissions[engine_id] += 1; if (q_ret) { client->q_fail += 1; client->retcode = q_ret; @@ -615,8 +586,8 @@ int i915_guc_submit(struct i915_guc_client *client, } else { client->retcode = 0; } - guc->submissions[ring_id] += 1; - guc->last_seqno[ring_id] = rq->seqno; + guc->submissions[engine_id] += 1; + guc->last_seqno[engine_id] = rq->seqno; return q_ret; } @@ -848,7 +819,7 @@ static void init_guc_policies(struct guc_policies *policies) policies->max_num_work_items = POLICY_MAX_NUM_WI; for (p = 0; p < GUC_CTX_PRIORITY_NUM; p++) { - for (i = 0; i < I915_NUM_RINGS; i++) { + for (i = GUC_RENDER_ENGINE; i < GUC_MAX_ENGINES_NUM; i++) { policy = &policies->policy[p][i]; policy->execution_quantum = 1000000; @@ -900,7 +871,7 @@ static void guc_create_ads(struct intel_guc *guc) ads->golden_context_lrca = ring->status_page.gfx_addr; for_each_ring(ring, dev_priv, i) - ads->eng_state_size[i] = intel_lr_context_size(ring); + ads->eng_state_size[ring->guc_id] = intel_lr_context_size(ring); /* GuC scheduling policies */ policies = (void *)ads + sizeof(struct guc_ads); @@ -912,12 +883,12 @@ static void guc_create_ads(struct intel_guc *guc) /* MMIO reg state */ reg_state = (void *)policies + sizeof(struct guc_policies); - for (i = 0; i < I915_NUM_RINGS; i++) { - reg_state->mmio_white_list[i].mmio_start = - dev_priv->ring[i].mmio_base + GUC_MMIO_WHITE_LIST_START; + for_each_ring(ring, dev_priv, i) { + reg_state->mmio_white_list[ring->guc_id].mmio_start = + ring->mmio_base + GUC_MMIO_WHITE_LIST_START; /* Nothing to be saved or restored for now. */ - reg_state->mmio_white_list[i].count = 0; + reg_state->mmio_white_list[ring->guc_id].count = 0; } ads->reg_state_addr = ads->scheduler_policies + diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 25a89373df63..d1a46ef5ab3f 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1651,6 +1651,12 @@ static void valleyview_pipestat_irq_handler(struct drm_device *dev, u32 iir) int pipe; spin_lock(&dev_priv->irq_lock); + + if (!dev_priv->display_irqs_enabled) { + spin_unlock(&dev_priv->irq_lock); + return; + } + for_each_pipe(dev_priv, pipe) { i915_reg_t reg; u32 mask, iir_bit = 0; @@ -3343,21 +3349,28 @@ void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv, unsigned int pipe_mask) { uint32_t extra_ier = GEN8_PIPE_VBLANK | GEN8_PIPE_FIFO_UNDERRUN; + enum pipe pipe; + + spin_lock_irq(&dev_priv->irq_lock); + for_each_pipe_masked(dev_priv, pipe, pipe_mask) + GEN8_IRQ_INIT_NDX(DE_PIPE, pipe, + dev_priv->de_irq_mask[pipe], + ~dev_priv->de_irq_mask[pipe] | extra_ier); + spin_unlock_irq(&dev_priv->irq_lock); +} + +void gen8_irq_power_well_pre_disable(struct drm_i915_private *dev_priv, + unsigned int pipe_mask) +{ + enum pipe pipe; spin_lock_irq(&dev_priv->irq_lock); - if (pipe_mask & 1 << PIPE_A) - GEN8_IRQ_INIT_NDX(DE_PIPE, PIPE_A, - dev_priv->de_irq_mask[PIPE_A], - ~dev_priv->de_irq_mask[PIPE_A] | extra_ier); - if (pipe_mask & 1 << PIPE_B) - GEN8_IRQ_INIT_NDX(DE_PIPE, PIPE_B, - dev_priv->de_irq_mask[PIPE_B], - ~dev_priv->de_irq_mask[PIPE_B] | extra_ier); - if (pipe_mask & 1 << PIPE_C) - GEN8_IRQ_INIT_NDX(DE_PIPE, PIPE_C, - dev_priv->de_irq_mask[PIPE_C], - ~dev_priv->de_irq_mask[PIPE_C] | extra_ier); + for_each_pipe_masked(dev_priv, pipe, pipe_mask) + GEN8_IRQ_RESET_NDX(DE_PIPE, pipe); spin_unlock_irq(&dev_priv->irq_lock); + + /* make sure we're done processing display irqs */ + synchronize_irq(dev_priv->dev->irq); } static void cherryview_irq_preinstall(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 8d90c256520a..278c9c40c2e0 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -38,7 +38,7 @@ struct i915_params i915 __read_mostly = { .enable_execlists = -1, .enable_hangcheck = true, .enable_ppgtt = -1, - .enable_psr = 0, + .enable_psr = -1, .preliminary_hw_support = IS_ENABLED(CONFIG_DRM_I915_PRELIMINARY_HW_SUPPORT), .disable_power_well = -1, .enable_ips = 1, @@ -49,7 +49,6 @@ struct i915_params i915 __read_mostly = { .invert_brightness = 0, .disable_display = 0, .enable_cmd_parser = 1, - .disable_vtd_wa = 0, .use_mmio_flip = 0, .mmio_debug = 0, .verbose_state_checks = 1, @@ -92,7 +91,7 @@ MODULE_PARM_DESC(enable_fbc, "Enable frame buffer compression for power savings " "(default: -1 (use per-chip default))"); -module_param_named_unsafe(lvds_channel_mode, i915.lvds_channel_mode, int, 0600); +module_param_named_unsafe(lvds_channel_mode, i915.lvds_channel_mode, int, 0400); MODULE_PARM_DESC(lvds_channel_mode, "Specify LVDS channel mode " "(0=probe BIOS [default], 1=single-channel, 2=dual-channel)"); @@ -102,7 +101,7 @@ MODULE_PARM_DESC(lvds_use_ssc, "Use Spread Spectrum Clock with panels [LVDS/eDP] " "(default: auto from VBT)"); -module_param_named_unsafe(vbt_sdvo_panel_type, i915.vbt_sdvo_panel_type, int, 0600); +module_param_named_unsafe(vbt_sdvo_panel_type, i915.vbt_sdvo_panel_type, int, 0400); MODULE_PARM_DESC(vbt_sdvo_panel_type, "Override/Ignore selection of SDVO panel mode in the VBT " "(-2=ignore, -1=auto [default], index in VBT BIOS table)"); @@ -127,9 +126,11 @@ MODULE_PARM_DESC(enable_execlists, "(-1=auto [default], 0=disabled, 1=enabled)"); module_param_named_unsafe(enable_psr, i915.enable_psr, int, 0600); -MODULE_PARM_DESC(enable_psr, "Enable PSR (default: false)"); +MODULE_PARM_DESC(enable_psr, "Enable PSR " + "(0=disabled, 1=enabled - link mode chosen per-platform, 2=force link-standby mode, 3=force link-off mode) " + "Default: -1 (use per-chip default)"); -module_param_named_unsafe(preliminary_hw_support, i915.preliminary_hw_support, int, 0600); +module_param_named_unsafe(preliminary_hw_support, i915.preliminary_hw_support, int, 0400); MODULE_PARM_DESC(preliminary_hw_support, "Enable preliminary hardware support."); @@ -163,12 +164,9 @@ MODULE_PARM_DESC(invert_brightness, "to dri-devel@lists.freedesktop.org, if your machine needs it. " "It will then be included in an upcoming module version."); -module_param_named(disable_display, i915.disable_display, bool, 0600); +module_param_named(disable_display, i915.disable_display, bool, 0400); MODULE_PARM_DESC(disable_display, "Disable display (default: false)"); -module_param_named_unsafe(disable_vtd_wa, i915.disable_vtd_wa, bool, 0600); -MODULE_PARM_DESC(disable_vtd_wa, "Disable all VT-d workarounds (default: false)"); - module_param_named_unsafe(enable_cmd_parser, i915.enable_cmd_parser, int, 0600); MODULE_PARM_DESC(enable_cmd_parser, "Enable command parsing (1=enabled [default], 0=disabled)"); diff --git a/drivers/gpu/drm/i915/i915_params.h b/drivers/gpu/drm/i915/i915_params.h index 529929073120..bd5026b15d3e 100644 --- a/drivers/gpu/drm/i915/i915_params.h +++ b/drivers/gpu/drm/i915/i915_params.h @@ -56,7 +56,6 @@ struct i915_params { bool load_detect_test; bool reset; bool disable_display; - bool disable_vtd_wa; bool enable_guc_submission; bool verbose_state_checks; bool nuclear_pageflip; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 0a988895165f..f76cbf3e5d1e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -610,16 +610,17 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg) #define IOSF_BYTE_ENABLES_SHIFT 4 #define IOSF_BAR_SHIFT 1 #define IOSF_SB_BUSY (1<<0) -#define IOSF_PORT_BUNIT 0x3 -#define IOSF_PORT_PUNIT 0x4 +#define IOSF_PORT_BUNIT 0x03 +#define IOSF_PORT_PUNIT 0x04 #define IOSF_PORT_NC 0x11 #define IOSF_PORT_DPIO 0x12 -#define IOSF_PORT_DPIO_2 0x1a #define IOSF_PORT_GPIO_NC 0x13 #define IOSF_PORT_CCK 0x14 -#define IOSF_PORT_CCU 0xA9 -#define IOSF_PORT_GPS_CORE 0x48 -#define IOSF_PORT_FLISDSI 0x1B +#define IOSF_PORT_DPIO_2 0x1a +#define IOSF_PORT_FLISDSI 0x1b +#define IOSF_PORT_GPIO_SC 0x48 +#define IOSF_PORT_GPIO_SUS 0xa8 +#define IOSF_PORT_CCU 0xa9 #define VLV_IOSF_DATA _MMIO(VLV_DISPLAY_BASE + 0x2104) #define VLV_IOSF_ADDR _MMIO(VLV_DISPLAY_BASE + 0x2108) @@ -1635,6 +1636,9 @@ enum skl_disp_power_wells { #define RING_WAIT (1<<11) /* gen3+, PRBx_CTL */ #define RING_WAIT_SEMAPHORE (1<<10) /* gen6+ */ +#define RING_FORCE_TO_NONPRIV(base, i) _MMIO(((base)+0x4D0) + (i)*4) +#define RING_MAX_NONPRIV_SLOTS 12 + #define GEN7_TLB_RD_ADDR _MMIO(0x4700) #if 0 @@ -3292,19 +3296,20 @@ enum skl_disp_power_wells { #define PORT_HOTPLUG_STAT _MMIO(dev_priv->info.display_mmio_offset + 0x61114) /* - * HDMI/DP bits are gen4+ + * HDMI/DP bits are g4x+ * * WARNING: Bspec for hpd status bits on gen4 seems to be completely confused. * Please check the detailed lore in the commit message for for experimental * evidence. */ -#define PORTD_HOTPLUG_LIVE_STATUS_G4X (1 << 29) +/* Bspec says GM45 should match G4X/VLV/CHV, but reality disagrees */ +#define PORTD_HOTPLUG_LIVE_STATUS_GM45 (1 << 29) +#define PORTC_HOTPLUG_LIVE_STATUS_GM45 (1 << 28) +#define PORTB_HOTPLUG_LIVE_STATUS_GM45 (1 << 27) +/* G4X/VLV/CHV DP/HDMI bits again match Bspec */ +#define PORTD_HOTPLUG_LIVE_STATUS_G4X (1 << 27) #define PORTC_HOTPLUG_LIVE_STATUS_G4X (1 << 28) -#define PORTB_HOTPLUG_LIVE_STATUS_G4X (1 << 27) -/* VLV DP/HDMI bits again match Bspec */ -#define PORTD_HOTPLUG_LIVE_STATUS_VLV (1 << 27) -#define PORTC_HOTPLUG_LIVE_STATUS_VLV (1 << 28) -#define PORTB_HOTPLUG_LIVE_STATUS_VLV (1 << 29) +#define PORTB_HOTPLUG_LIVE_STATUS_G4X (1 << 29) #define PORTD_HOTPLUG_INT_STATUS (3 << 21) #define PORTD_HOTPLUG_INT_LONG_PULSE (2 << 21) #define PORTD_HOTPLUG_INT_SHORT_PULSE (1 << 21) @@ -5945,6 +5950,7 @@ enum skl_disp_power_wells { #define ILK_INTERNAL_GRAPHICS_DISABLE (1 << 31) #define ILK_INTERNAL_DISPLAY_DISABLE (1 << 30) #define ILK_DISPLAY_DEBUG_DISABLE (1 << 29) +#define IVB_PIPE_C_DISABLE (1 << 28) #define ILK_HDCP_DISABLE (1 << 25) #define ILK_eDP_A_DISABLE (1 << 24) #define HSW_CDCLK_LIMIT (1 << 24) @@ -5991,10 +5997,19 @@ enum skl_disp_power_wells { #define SKL_DFSM_CDCLK_LIMIT_540 (1 << 23) #define SKL_DFSM_CDCLK_LIMIT_450 (2 << 23) #define SKL_DFSM_CDCLK_LIMIT_337_5 (3 << 23) +#define SKL_DFSM_PIPE_A_DISABLE (1 << 30) +#define SKL_DFSM_PIPE_B_DISABLE (1 << 21) +#define SKL_DFSM_PIPE_C_DISABLE (1 << 28) + +#define GEN7_FF_SLICE_CS_CHICKEN1 _MMIO(0x20e0) +#define GEN9_FFSC_PERCTX_PREEMPT_CTRL (1<<14) #define FF_SLICE_CS_CHICKEN2 _MMIO(0x20e4) #define GEN9_TSG_BARRIER_ACK_DISABLE (1<<8) +#define GEN9_CS_DEBUG_MODE1 _MMIO(0x20ec) +#define GEN8_CS_CHICKEN1 _MMIO(0x2580) + /* GEN7 chicken */ #define GEN7_COMMON_SLICE_CHICKEN1 _MMIO(0x7010) # define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC ((1<<10) | (1<<26)) @@ -6040,6 +6055,8 @@ enum skl_disp_power_wells { #define HDC_FORCE_NON_COHERENT (1<<4) #define HDC_BARRIER_PERFORMANCE_DISABLE (1<<10) +#define GEN8_HDC_CHICKEN1 _MMIO(0x7304) + /* GEN9 chicken */ #define SLICE_ECO_CHICKEN0 _MMIO(0x7308) #define PIXEL_MASK_CAMMING_DISABLE (1 << 14) @@ -6770,6 +6787,16 @@ enum skl_disp_power_wells { #define VLV_PMWGICZ _MMIO(0x1300a4) +#define RC6_LOCATION _MMIO(0xD40) +#define RC6_CTX_IN_DRAM (1 << 0) +#define RC6_CTX_BASE _MMIO(0xD48) +#define RC6_CTX_BASE_MASK 0xFFFFFFF0 +#define PWRCTX_MAXCNT_RCSUNIT _MMIO(0x2054) +#define PWRCTX_MAXCNT_VCSUNIT0 _MMIO(0x12054) +#define PWRCTX_MAXCNT_BCSUNIT _MMIO(0x22054) +#define PWRCTX_MAXCNT_VECSUNIT _MMIO(0x1A054) +#define PWRCTX_MAXCNT_VCSUNIT1 _MMIO(0x1C054) +#define IDLE_TIME_MASK 0xFFFFF #define FORCEWAKE _MMIO(0xA18C) #define FORCEWAKE_VLV _MMIO(0x1300b0) #define FORCEWAKE_ACK_VLV _MMIO(0x1300b4) @@ -6908,6 +6935,7 @@ enum skl_disp_power_wells { #define GEN6_RPDEUC _MMIO(0xA084) #define GEN6_RPDEUCSW _MMIO(0xA088) #define GEN6_RC_STATE _MMIO(0xA094) +#define RC6_STATE (1 << 18) #define GEN6_RC1_WAKE_RATE_LIMIT _MMIO(0xA098) #define GEN6_RC6_WAKE_RATE_LIMIT _MMIO(0xA09C) #define GEN6_RC6pp_WAKE_RATE_LIMIT _MMIO(0xA0A0) @@ -7519,7 +7547,7 @@ enum skl_disp_power_wells { #define DPLL_CFGCR2_PDIV_7 (4<<2) #define DPLL_CFGCR2_CENTRAL_FREQ_MASK (3) -#define DPLL_CFGCR1(id) _MMIO_PIPE((id) - SKL_DPLL1, _DPLL1_CFGCR1, _DPLL2_CFGCR2) +#define DPLL_CFGCR1(id) _MMIO_PIPE((id) - SKL_DPLL1, _DPLL1_CFGCR1, _DPLL2_CFGCR1) #define DPLL_CFGCR2(id) _MMIO_PIPE((id) - SKL_DPLL1, _DPLL1_CFGCR2, _DPLL2_CFGCR2) /* BXT display engine PLL */ @@ -7540,6 +7568,7 @@ enum skl_disp_power_wells { #define DC_STATE_EN_UPTO_DC5_DC6_MASK 0x3 #define DC_STATE_DEBUG _MMIO(0x45520) +#define DC_STATE_DEBUG_MASK_CORES (1<<0) #define DC_STATE_DEBUG_MASK_MEMORY_UP (1<<1) /* Please see hsw_read_dcomp() and hsw_write_dcomp() before using this register, @@ -8159,4 +8188,11 @@ enum skl_disp_power_wells { #define GEN9_VEBOX_MOCS(i) _MMIO(0xcb00 + (i) * 4) /* Video MOCS registers */ #define GEN9_BLT_MOCS(i) _MMIO(0xcc00 + (i) * 4) /* Blitter MOCS registers */ +/* gamt regs */ +#define GEN8_L3_LRA_1_GPGPU _MMIO(0x4dd4) +#define GEN8_L3_LRA_1_GPGPU_DEFAULT_VALUE_BDW 0x67F1427F /* max/min for LRA1/2 */ +#define GEN8_L3_LRA_1_GPGPU_DEFAULT_VALUE_CHV 0x5FF101FF /* max/min for LRA1/2 */ +#define GEN9_L3_LRA_1_GPGPU_DEFAULT_VALUE_SKL 0x67F1427F /* " " */ +#define GEN9_L3_LRA_1_GPGPU_DEFAULT_VALUE_BXT 0x5FF101FF /* " " */ + #endif /* _I915_REG_H_ */ diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index a2aa09ce3202..34e061a9ef06 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -49,7 +49,7 @@ static void i915_save_display(struct drm_device *dev) dev_priv->regfile.savePP_ON_DELAYS = I915_READ(PCH_PP_ON_DELAYS); dev_priv->regfile.savePP_OFF_DELAYS = I915_READ(PCH_PP_OFF_DELAYS); dev_priv->regfile.savePP_DIVISOR = I915_READ(PCH_PP_DIVISOR); - } else if (!IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev)) { + } else if (INTEL_INFO(dev)->gen <= 4) { dev_priv->regfile.savePP_CONTROL = I915_READ(PP_CONTROL); dev_priv->regfile.savePP_ON_DELAYS = I915_READ(PP_ON_DELAYS); dev_priv->regfile.savePP_OFF_DELAYS = I915_READ(PP_OFF_DELAYS); @@ -84,7 +84,7 @@ static void i915_restore_display(struct drm_device *dev) I915_WRITE(PCH_PP_OFF_DELAYS, dev_priv->regfile.savePP_OFF_DELAYS); I915_WRITE(PCH_PP_DIVISOR, dev_priv->regfile.savePP_DIVISOR); I915_WRITE(PCH_PP_CONTROL, dev_priv->regfile.savePP_CONTROL); - } else if (!IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev)) { + } else if (INTEL_INFO(dev)->gen <= 4) { I915_WRITE(PP_ON_DELAYS, dev_priv->regfile.savePP_ON_DELAYS); I915_WRITE(PP_OFF_DELAYS, dev_priv->regfile.savePP_OFF_DELAYS); I915_WRITE(PP_DIVISOR, dev_priv->regfile.savePP_DIVISOR); @@ -92,7 +92,7 @@ static void i915_restore_display(struct drm_device *dev) } /* only restore FBC info on the platform that supports FBC*/ - intel_fbc_disable(dev_priv); + intel_fbc_global_disable(dev_priv); /* restore FBC interval */ if (HAS_FBC(dev) && INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev)) diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index 52b2d409945d..fa09e5581137 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -175,35 +175,24 @@ TRACE_EVENT(i915_vma_unbind, __entry->obj, __entry->offset, __entry->size, __entry->vm) ); -#define VM_TO_TRACE_NAME(vm) \ - (i915_is_ggtt(vm) ? "G" : \ - "P") - -DECLARE_EVENT_CLASS(i915_va, - TP_PROTO(struct i915_address_space *vm, u64 start, u64 length, const char *name), - TP_ARGS(vm, start, length, name), +TRACE_EVENT(i915_va_alloc, + TP_PROTO(struct i915_vma *vma), + TP_ARGS(vma), TP_STRUCT__entry( __field(struct i915_address_space *, vm) __field(u64, start) __field(u64, end) - __string(name, name) ), TP_fast_assign( - __entry->vm = vm; - __entry->start = start; - __entry->end = start + length - 1; - __assign_str(name, name); + __entry->vm = vma->vm; + __entry->start = vma->node.start; + __entry->end = vma->node.start + vma->node.size - 1; ), - TP_printk("vm=%p (%s), 0x%llx-0x%llx", - __entry->vm, __get_str(name), __entry->start, __entry->end) -); - -DEFINE_EVENT(i915_va, i915_va_alloc, - TP_PROTO(struct i915_address_space *vm, u64 start, u64 length, const char *name), - TP_ARGS(vm, start, length, name) + TP_printk("vm=%p (%c), 0x%llx-0x%llx", + __entry->vm, i915_is_ggtt(__entry->vm) ? 'G' : 'P', __entry->start, __entry->end) ); DECLARE_EVENT_CLASS(i915_px_entry, diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 4625f8a9ba12..8e579a8505ac 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -97,6 +97,7 @@ intel_crtc_duplicate_state(struct drm_crtc *crtc) crtc_state->disable_lp_wm = false; crtc_state->disable_cxsr = false; crtc_state->wm_changed = false; + crtc_state->fb_changed = false; return &crtc_state->base; } diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 9c89df1af036..505fc5cf26f8 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -71,22 +71,29 @@ static bool intel_crt_get_hw_state(struct intel_encoder *encoder, struct intel_crt *crt = intel_encoder_to_crt(encoder); enum intel_display_power_domain power_domain; u32 tmp; + bool ret; power_domain = intel_display_port_power_domain(encoder); - if (!intel_display_power_is_enabled(dev_priv, power_domain)) + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) return false; + ret = false; + tmp = I915_READ(crt->adpa_reg); if (!(tmp & ADPA_DAC_ENABLE)) - return false; + goto out; if (HAS_PCH_CPT(dev)) *pipe = PORT_TO_PIPE_CPT(tmp); else *pipe = PORT_TO_PIPE(tmp); - return true; + ret = true; +out: + intel_display_power_put(dev_priv, power_domain); + + return ret; } static unsigned int intel_crt_get_flags(struct intel_encoder *encoder) @@ -206,9 +213,7 @@ static void pch_post_disable_crt(struct intel_encoder *encoder) static void intel_enable_crt(struct intel_encoder *encoder) { - struct intel_crt *crt = intel_encoder_to_crt(encoder); - - intel_crt_set_dpms(encoder, crt->connector->base.dpms); + intel_crt_set_dpms(encoder, DRM_MODE_DPMS_ON); } static enum drm_mode_status @@ -216,6 +221,7 @@ intel_crt_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct drm_device *dev = connector->dev; + int max_dotclk = to_i915(dev)->max_dotclk_freq; int max_clock = 0; if (mode->flags & DRM_MODE_FLAG_DBLSCAN) @@ -231,6 +237,9 @@ intel_crt_mode_valid(struct drm_connector *connector, if (mode->clock > max_clock) return MODE_CLOCK_HIGH; + if (mode->clock > max_dotclk) + return MODE_CLOCK_HIGH; + /* The FDI receiver on LPT only supports 8bpc and only has 2 lanes. */ if (HAS_PCH_LPT(dev) && (ironlake_get_lanes_required(mode->clock, 270000, 24) > 2)) @@ -469,11 +478,10 @@ static bool intel_crt_detect_ddc(struct drm_connector *connector) } static enum drm_connector_status -intel_crt_load_detect(struct intel_crt *crt) +intel_crt_load_detect(struct intel_crt *crt, uint32_t pipe) { struct drm_device *dev = crt->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - uint32_t pipe = to_intel_crtc(crt->base.base.crtc)->pipe; uint32_t save_bclrpat; uint32_t save_vtotal; uint32_t vtotal, vactive; @@ -642,7 +650,8 @@ intel_crt_detect(struct drm_connector *connector, bool force) if (intel_crt_detect_ddc(connector)) status = connector_status_connected; else if (INTEL_INFO(dev)->gen < 4) - status = intel_crt_load_detect(crt); + status = intel_crt_load_detect(crt, + to_intel_crtc(connector->state->crtc)->pipe); else status = connector_status_unknown; intel_release_load_detect_pipe(connector, &tmp, &ctx); diff --git a/drivers/gpu/drm/i915/intel_csr.c b/drivers/gpu/drm/i915/intel_csr.c index 5c2f9a40c81b..902054efb902 100644 --- a/drivers/gpu/drm/i915/intel_csr.c +++ b/drivers/gpu/drm/i915/intel_csr.c @@ -179,7 +179,8 @@ static const struct stepping_info kbl_stepping_info[] = { static const struct stepping_info skl_stepping_info[] = { {'A', '0'}, {'B', '0'}, {'C', '0'}, {'D', '0'}, {'E', '0'}, {'F', '0'}, - {'G', '0'}, {'H', '0'}, {'I', '0'} + {'G', '0'}, {'H', '0'}, {'I', '0'}, + {'J', '0'}, {'K', '0'} }; static const struct stepping_info bxt_stepping_info[] = { @@ -219,19 +220,19 @@ static const struct stepping_info *intel_get_stepping_info(struct drm_device *de * Everytime display comes back from low power state this function is called to * copy the firmware from internal memory to registers. */ -void intel_csr_load_program(struct drm_i915_private *dev_priv) +bool intel_csr_load_program(struct drm_i915_private *dev_priv) { u32 *payload = dev_priv->csr.dmc_payload; uint32_t i, fw_size; if (!IS_GEN9(dev_priv)) { DRM_ERROR("No CSR support available for this platform\n"); - return; + return false; } if (!dev_priv->csr.dmc_payload) { DRM_ERROR("Tried to program CSR with empty payload\n"); - return; + return false; } fw_size = dev_priv->csr.dmc_fw_size; @@ -242,6 +243,10 @@ void intel_csr_load_program(struct drm_i915_private *dev_priv) I915_WRITE(dev_priv->csr.mmioaddr[i], dev_priv->csr.mmiodata[i]); } + + dev_priv->csr.dc_state = 0; + + return true; } static uint32_t *parse_csr_fw(struct drm_i915_private *dev_priv, diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 1f9a3687b540..21a9b83f3bfc 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1531,7 +1531,8 @@ skl_ddi_pll_select(struct intel_crtc *intel_crtc, DPLL_CFGCR2_KDIV(wrpll_params.kdiv) | DPLL_CFGCR2_PDIV(wrpll_params.pdiv) | wrpll_params.central_freq; - } else if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) { + } else if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT || + intel_encoder->type == INTEL_OUTPUT_DP_MST) { switch (crtc_state->port_clock / 2) { case 81000: ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_810, 0); @@ -1545,8 +1546,10 @@ skl_ddi_pll_select(struct intel_crtc *intel_crtc, } cfgcr1 = cfgcr2 = 0; - } else /* eDP */ + } else if (intel_encoder->type == INTEL_OUTPUT_EDP) { return true; + } else + return false; memset(&crtc_state->dpll_hw_state, 0, sizeof(crtc_state->dpll_hw_state)); @@ -1910,13 +1913,16 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector) enum transcoder cpu_transcoder; enum intel_display_power_domain power_domain; uint32_t tmp; + bool ret; power_domain = intel_display_port_power_domain(intel_encoder); - if (!intel_display_power_is_enabled(dev_priv, power_domain)) + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) return false; - if (!intel_encoder->get_hw_state(intel_encoder, &pipe)) - return false; + if (!intel_encoder->get_hw_state(intel_encoder, &pipe)) { + ret = false; + goto out; + } if (port == PORT_A) cpu_transcoder = TRANSCODER_EDP; @@ -1928,23 +1934,33 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector) switch (tmp & TRANS_DDI_MODE_SELECT_MASK) { case TRANS_DDI_MODE_SELECT_HDMI: case TRANS_DDI_MODE_SELECT_DVI: - return (type == DRM_MODE_CONNECTOR_HDMIA); + ret = type == DRM_MODE_CONNECTOR_HDMIA; + break; case TRANS_DDI_MODE_SELECT_DP_SST: - if (type == DRM_MODE_CONNECTOR_eDP) - return true; - return (type == DRM_MODE_CONNECTOR_DisplayPort); + ret = type == DRM_MODE_CONNECTOR_eDP || + type == DRM_MODE_CONNECTOR_DisplayPort; + break; + case TRANS_DDI_MODE_SELECT_DP_MST: /* if the transcoder is in MST state then * connector isn't connected */ - return false; + ret = false; + break; case TRANS_DDI_MODE_SELECT_FDI: - return (type == DRM_MODE_CONNECTOR_VGA); + ret = type == DRM_MODE_CONNECTOR_VGA; + break; default: - return false; + ret = false; + break; } + +out: + intel_display_power_put(dev_priv, power_domain); + + return ret; } bool intel_ddi_get_hw_state(struct intel_encoder *encoder, @@ -1956,15 +1972,18 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder, enum intel_display_power_domain power_domain; u32 tmp; int i; + bool ret; power_domain = intel_display_port_power_domain(encoder); - if (!intel_display_power_is_enabled(dev_priv, power_domain)) + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) return false; + ret = false; + tmp = I915_READ(DDI_BUF_CTL(port)); if (!(tmp & DDI_BUF_CTL_ENABLE)) - return false; + goto out; if (port == PORT_A) { tmp = I915_READ(TRANS_DDI_FUNC_CTL(TRANSCODER_EDP)); @@ -1982,25 +2001,32 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder, break; } - return true; - } else { - for (i = TRANSCODER_A; i <= TRANSCODER_C; i++) { - tmp = I915_READ(TRANS_DDI_FUNC_CTL(i)); + ret = true; - if ((tmp & TRANS_DDI_PORT_MASK) - == TRANS_DDI_SELECT_PORT(port)) { - if ((tmp & TRANS_DDI_MODE_SELECT_MASK) == TRANS_DDI_MODE_SELECT_DP_MST) - return false; + goto out; + } - *pipe = i; - return true; - } + for (i = TRANSCODER_A; i <= TRANSCODER_C; i++) { + tmp = I915_READ(TRANS_DDI_FUNC_CTL(i)); + + if ((tmp & TRANS_DDI_PORT_MASK) == TRANS_DDI_SELECT_PORT(port)) { + if ((tmp & TRANS_DDI_MODE_SELECT_MASK) == + TRANS_DDI_MODE_SELECT_DP_MST) + goto out; + + *pipe = i; + ret = true; + + goto out; } } DRM_DEBUG_KMS("No pipe for ddi port %c found\n", port_name(port)); - return false; +out: + intel_display_power_put(dev_priv, power_domain); + + return ret; } void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc) @@ -2446,12 +2472,14 @@ static bool hsw_ddi_wrpll_get_hw_state(struct drm_i915_private *dev_priv, { uint32_t val; - if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PLLS)) + if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS)) return false; val = I915_READ(WRPLL_CTL(pll->id)); hw_state->wrpll = val; + intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS); + return val & WRPLL_PLL_ENABLE; } @@ -2461,12 +2489,14 @@ static bool hsw_ddi_spll_get_hw_state(struct drm_i915_private *dev_priv, { uint32_t val; - if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PLLS)) + if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS)) return false; val = I915_READ(SPLL_CTL); hw_state->spll = val; + intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS); + return val & SPLL_PLL_ENABLE; } @@ -2583,16 +2613,19 @@ static bool skl_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv, uint32_t val; unsigned int dpll; const struct skl_dpll_regs *regs = skl_dpll_regs; + bool ret; - if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PLLS)) + if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS)) return false; + ret = false; + /* DPLL0 is not part of the shared DPLLs, so pll->id is 0 for DPLL1 */ dpll = pll->id + 1; val = I915_READ(regs[pll->id].ctl); if (!(val & LCPLL_PLL_ENABLE)) - return false; + goto out; val = I915_READ(DPLL_CTRL1); hw_state->ctrl1 = (val >> (dpll * 6)) & 0x3f; @@ -2602,8 +2635,12 @@ static bool skl_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv, hw_state->cfgcr1 = I915_READ(regs[pll->id].cfgcr1); hw_state->cfgcr2 = I915_READ(regs[pll->id].cfgcr2); } + ret = true; - return true; +out: + intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS); + + return ret; } static void skl_shared_dplls_init(struct drm_i915_private *dev_priv) @@ -2870,13 +2907,16 @@ static bool bxt_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv, { enum port port = (enum port)pll->id; /* 1:1 port->PLL mapping */ uint32_t val; + bool ret; - if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PLLS)) + if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS)) return false; + ret = false; + val = I915_READ(BXT_PORT_PLL_ENABLE(port)); if (!(val & PORT_PLL_ENABLE)) - return false; + goto out; hw_state->ebb0 = I915_READ(BXT_PORT_PLL_EBB_0(port)); hw_state->ebb0 &= PORT_PLL_P1_MASK | PORT_PLL_P2_MASK; @@ -2923,7 +2963,12 @@ static bool bxt_ddi_pll_get_hw_state(struct drm_i915_private *dev_priv, I915_READ(BXT_PORT_PCS_DW12_LN23(port))); hw_state->pcsdw12 &= LANE_STAGGER_MASK | LANESTAGGER_STRAP_OVRD; - return true; + ret = true; + +out: + intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS); + + return ret; } static void bxt_shared_dplls_init(struct drm_i915_private *dev_priv) @@ -3058,11 +3103,15 @@ bool intel_ddi_is_audio_enabled(struct drm_i915_private *dev_priv, { u32 temp; - if (intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_AUDIO)) { + if (intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_AUDIO)) { temp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD); + + intel_display_power_put(dev_priv, POWER_DOMAIN_AUDIO); + if (temp & AUDIO_OUTPUT_ENABLE(intel_crtc->pipe)) return true; } + return false; } @@ -3281,7 +3330,6 @@ void intel_ddi_init(struct drm_device *dev, enum port port) intel_dig_port->saved_port_bits = I915_READ(DDI_BUF_CTL(port)) & (DDI_BUF_PORT_REVERSAL | DDI_A_4_LANES); - intel_dig_port->max_lanes = max_lanes; /* * Bspec says that DDI_A_4_LANES is the only supported configuration @@ -3294,9 +3342,12 @@ void intel_ddi_init(struct drm_device *dev, enum port port) if (!(intel_dig_port->saved_port_bits & DDI_A_4_LANES)) { DRM_DEBUG_KMS("BXT BIOS forgot to set DDI_A_4_LANES for port A; fixing\n"); intel_dig_port->saved_port_bits |= DDI_A_4_LANES; + max_lanes = 4; } } + intel_dig_port->max_lanes = max_lanes; + intel_encoder->type = INTEL_OUTPUT_UNKNOWN; intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); intel_encoder->cloneable = 0; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e1e7cdee3bbd..8b7b8b64b008 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1343,18 +1343,21 @@ void assert_pipe(struct drm_i915_private *dev_priv, bool cur_state; enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, pipe); + enum intel_display_power_domain power_domain; /* if we need the pipe quirk it must be always on */ if ((pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE) || (pipe == PIPE_B && dev_priv->quirks & QUIRK_PIPEB_FORCE)) state = true; - if (!intel_display_power_is_enabled(dev_priv, - POWER_DOMAIN_TRANSCODER(cpu_transcoder))) { - cur_state = false; - } else { + power_domain = POWER_DOMAIN_TRANSCODER(cpu_transcoder); + if (intel_display_power_get_if_enabled(dev_priv, power_domain)) { u32 val = I915_READ(PIPECONF(cpu_transcoder)); cur_state = !!(val & PIPECONF_ENABLE); + + intel_display_power_put(dev_priv, power_domain); + } else { + cur_state = false; } I915_STATE_WARN(cur_state != state, @@ -2284,7 +2287,7 @@ intel_fill_fb_ggtt_view(struct i915_ggtt_view *view, struct drm_framebuffer *fb, const struct drm_plane_state *plane_state) { struct drm_i915_private *dev_priv = to_i915(fb->dev); - struct intel_rotation_info *info = &view->params.rotation_info; + struct intel_rotation_info *info = &view->params.rotated; unsigned int tile_size, tile_width, tile_height, cpp; *view = i915_ggtt_view_normal; @@ -2306,7 +2309,7 @@ intel_fill_fb_ggtt_view(struct i915_ggtt_view *view, struct drm_framebuffer *fb, tile_size = intel_tile_size(dev_priv); cpp = drm_format_plane_cpp(fb->pixel_format, 0); - tile_width = intel_tile_width(dev_priv, cpp, fb->modifier[0]); + tile_width = intel_tile_width(dev_priv, fb->modifier[0], cpp); tile_height = tile_size / tile_width; info->width_pages = DIV_ROUND_UP(fb->pitches[0], tile_width); @@ -2448,11 +2451,11 @@ static void intel_unpin_fb_obj(struct drm_framebuffer *fb, /* Computes the linear offset to the base tile and adjusts x, y. bytes per pixel * is assumed to be a power-of-two. */ -unsigned long intel_compute_tile_offset(struct drm_i915_private *dev_priv, - int *x, int *y, - uint64_t fb_modifier, - unsigned int cpp, - unsigned int pitch) +u32 intel_compute_tile_offset(struct drm_i915_private *dev_priv, + int *x, int *y, + uint64_t fb_modifier, + unsigned int cpp, + unsigned int pitch) { if (fb_modifier != DRM_FORMAT_MOD_NONE) { unsigned int tile_size, tile_width, tile_height; @@ -2551,12 +2554,16 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc, if (size_aligned * 2 > dev_priv->gtt.stolen_usable_size) return false; + mutex_lock(&dev->struct_mutex); + obj = i915_gem_object_create_stolen_for_preallocated(dev, base_aligned, base_aligned, size_aligned); - if (!obj) + if (!obj) { + mutex_unlock(&dev->struct_mutex); return false; + } obj->tiling_mode = plane_config->tiling; if (obj->tiling_mode == I915_TILING_X) @@ -2569,12 +2576,12 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc, mode_cmd.modifier[0] = fb->modifier[0]; mode_cmd.flags = DRM_MODE_FB_MODIFIERS; - mutex_lock(&dev->struct_mutex); if (intel_framebuffer_init(dev, to_intel_framebuffer(fb), &mode_cmd, obj)) { DRM_DEBUG_KMS("intel fb init failed\n"); goto out_unref_obj; } + mutex_unlock(&dev->struct_mutex); DRM_DEBUG_KMS("initial plane fb obj %p\n", obj); @@ -2706,14 +2713,12 @@ static void i9xx_update_primary_plane(struct drm_plane *primary, struct drm_framebuffer *fb = plane_state->base.fb; struct drm_i915_gem_object *obj = intel_fb_obj(fb); int plane = intel_crtc->plane; - unsigned long linear_offset; - int x = plane_state->src.x1 >> 16; - int y = plane_state->src.y1 >> 16; + u32 linear_offset; u32 dspcntr; i915_reg_t reg = DSPCNTR(plane); - int pixel_size; - - pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + int cpp = drm_format_plane_cpp(fb->pixel_format, 0); + int x = plane_state->src.x1 >> 16; + int y = plane_state->src.y1 >> 16; dspcntr = DISPPLANE_GAMMA_ENABLE; @@ -2771,13 +2776,12 @@ static void i9xx_update_primary_plane(struct drm_plane *primary, if (IS_G4X(dev)) dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; - linear_offset = y * fb->pitches[0] + x * pixel_size; + linear_offset = y * fb->pitches[0] + x * cpp; if (INTEL_INFO(dev)->gen >= 4) { intel_crtc->dspaddr_offset = intel_compute_tile_offset(dev_priv, &x, &y, - fb->modifier[0], - pixel_size, + fb->modifier[0], cpp, fb->pitches[0]); linear_offset -= intel_crtc->dspaddr_offset; } else { @@ -2794,7 +2798,7 @@ static void i9xx_update_primary_plane(struct drm_plane *primary, data and adding to linear_offset*/ linear_offset += (crtc_state->pipe_src_h - 1) * fb->pitches[0] + - (crtc_state->pipe_src_w - 1) * pixel_size; + (crtc_state->pipe_src_w - 1) * cpp; } intel_crtc->adjusted_x = x; @@ -2839,10 +2843,10 @@ static void ironlake_update_primary_plane(struct drm_plane *primary, struct drm_framebuffer *fb = plane_state->base.fb; struct drm_i915_gem_object *obj = intel_fb_obj(fb); int plane = intel_crtc->plane; - unsigned long linear_offset; + u32 linear_offset; u32 dspcntr; i915_reg_t reg = DSPCNTR(plane); - int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + int cpp = drm_format_plane_cpp(fb->pixel_format, 0); int x = plane_state->src.x1 >> 16; int y = plane_state->src.y1 >> 16; @@ -2881,11 +2885,10 @@ static void ironlake_update_primary_plane(struct drm_plane *primary, if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE; - linear_offset = y * fb->pitches[0] + x * pixel_size; + linear_offset = y * fb->pitches[0] + x * cpp; intel_crtc->dspaddr_offset = intel_compute_tile_offset(dev_priv, &x, &y, - fb->modifier[0], - pixel_size, + fb->modifier[0], cpp, fb->pitches[0]); linear_offset -= intel_crtc->dspaddr_offset; if (plane_state->base.rotation == BIT(DRM_ROTATE_180)) { @@ -2899,7 +2902,7 @@ static void ironlake_update_primary_plane(struct drm_plane *primary, data and adding to linear_offset*/ linear_offset += (crtc_state->pipe_src_h - 1) * fb->pitches[0] + - (crtc_state->pipe_src_w - 1) * pixel_size; + (crtc_state->pipe_src_w - 1) * cpp; } } @@ -2951,7 +2954,7 @@ u32 intel_plane_obj_offset(struct intel_plane *intel_plane, offset = vma->node.start; if (plane == 1) { - offset += vma->ggtt_view.params.rotation_info.uv_start_page * + offset += vma->ggtt_view.params.rotated.uv_start_page * PAGE_SIZE; } @@ -3160,9 +3163,6 @@ static void skylake_disable_primary_plane(struct drm_plane *primary, struct drm_i915_private *dev_priv = dev->dev_private; int pipe = to_intel_crtc(crtc)->pipe; - if (dev_priv->fbc.deactivate) - dev_priv->fbc.deactivate(dev_priv); - I915_WRITE(PLANE_CTL(pipe, 0), 0); I915_WRITE(PLANE_SURF(pipe, 0), 0); POSTING_READ(PLANE_SURF(pipe, 0)); @@ -4792,9 +4792,6 @@ static void intel_post_plane_update(struct intel_crtc *crtc) to_intel_crtc_state(crtc->base.state); struct drm_device *dev = crtc->base.dev; - if (atomic->wait_vblank) - intel_wait_for_vblank(dev, crtc->pipe); - intel_frontbuffer_flip(dev, atomic->fb_bits); crtc->wm.cxsr_allowed = true; @@ -4803,7 +4800,7 @@ static void intel_post_plane_update(struct intel_crtc *crtc) intel_update_watermarks(&crtc->base); if (atomic->update_fbc) - intel_fbc_update(crtc); + intel_fbc_post_update(crtc); if (atomic->post_enable_primary) intel_post_enable_primary(&crtc->base); @@ -4811,26 +4808,39 @@ static void intel_post_plane_update(struct intel_crtc *crtc) memset(atomic, 0, sizeof(*atomic)); } -static void intel_pre_plane_update(struct intel_crtc *crtc) +static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state) { + struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->base.crtc); struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc_atomic_commit *atomic = &crtc->atomic; struct intel_crtc_state *pipe_config = to_intel_crtc_state(crtc->base.state); + struct drm_atomic_state *old_state = old_crtc_state->base.state; + struct drm_plane *primary = crtc->base.primary; + struct drm_plane_state *old_pri_state = + drm_atomic_get_existing_plane_state(old_state, primary); + bool modeset = needs_modeset(&pipe_config->base); - if (atomic->disable_fbc) - intel_fbc_deactivate(crtc); + if (atomic->update_fbc) + intel_fbc_pre_update(crtc); - if (crtc->atomic.disable_ips) - hsw_disable_ips(crtc); + if (old_pri_state) { + struct intel_plane_state *primary_state = + to_intel_plane_state(primary->state); + struct intel_plane_state *old_primary_state = + to_intel_plane_state(old_pri_state); - if (atomic->pre_disable_primary) - intel_pre_disable_primary(&crtc->base); + if (old_primary_state->visible && + (modeset || !primary_state->visible)) + intel_pre_disable_primary(&crtc->base); + } if (pipe_config->disable_cxsr) { crtc->wm.cxsr_allowed = false; - intel_set_memory_cxsr(dev_priv, false); + + if (old_crtc_state->base.active) + intel_set_memory_cxsr(dev_priv, false); } if (!needs_modeset(&pipe_config->base) && pipe_config->wm_changed) @@ -4931,8 +4941,6 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) if (intel_crtc->config->has_pch_encoder) intel_wait_for_vblank(dev, pipe); intel_set_pch_fifo_underrun_reporting(dev_priv, pipe, true); - - intel_fbc_enable(intel_crtc); } /* IPS only exists on ULT machines and is tied to pipe A. */ @@ -5045,8 +5053,6 @@ static void haswell_crtc_enable(struct drm_crtc *crtc) intel_wait_for_vblank(dev, hsw_workaround_pipe); intel_wait_for_vblank(dev, hsw_workaround_pipe); } - - intel_fbc_enable(intel_crtc); } static void ironlake_pfit_disable(struct intel_crtc *crtc, bool force) @@ -5127,8 +5133,6 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) } intel_set_pch_fifo_underrun_reporting(dev_priv, pipe, true); - - intel_fbc_disable_crtc(intel_crtc); } static void haswell_crtc_disable(struct drm_crtc *crtc) @@ -5179,8 +5183,6 @@ static void haswell_crtc_disable(struct drm_crtc *crtc) intel_set_pch_fifo_underrun_reporting(dev_priv, TRANSCODER_A, true); } - - intel_fbc_disable_crtc(intel_crtc); } static void i9xx_pfit_enable(struct intel_crtc *crtc) @@ -5303,31 +5305,37 @@ intel_display_port_aux_power_domain(struct intel_encoder *intel_encoder) } } -static unsigned long get_crtc_power_domains(struct drm_crtc *crtc) +static unsigned long get_crtc_power_domains(struct drm_crtc *crtc, + struct intel_crtc_state *crtc_state) { struct drm_device *dev = crtc->dev; - struct intel_encoder *intel_encoder; + struct drm_encoder *encoder; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); enum pipe pipe = intel_crtc->pipe; unsigned long mask; - enum transcoder transcoder = intel_crtc->config->cpu_transcoder; + enum transcoder transcoder = crtc_state->cpu_transcoder; - if (!crtc->state->active) + if (!crtc_state->base.active) return 0; mask = BIT(POWER_DOMAIN_PIPE(pipe)); mask |= BIT(POWER_DOMAIN_TRANSCODER(transcoder)); - if (intel_crtc->config->pch_pfit.enabled || - intel_crtc->config->pch_pfit.force_thru) + if (crtc_state->pch_pfit.enabled || + crtc_state->pch_pfit.force_thru) mask |= BIT(POWER_DOMAIN_PIPE_PANEL_FITTER(pipe)); - for_each_encoder_on_crtc(dev, crtc, intel_encoder) + drm_for_each_encoder_mask(encoder, dev, crtc_state->base.encoder_mask) { + struct intel_encoder *intel_encoder = to_intel_encoder(encoder); + mask |= BIT(intel_display_port_power_domain(intel_encoder)); + } return mask; } -static unsigned long modeset_get_crtc_power_domains(struct drm_crtc *crtc) +static unsigned long +modeset_get_crtc_power_domains(struct drm_crtc *crtc, + struct intel_crtc_state *crtc_state) { struct drm_i915_private *dev_priv = crtc->dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -5335,7 +5343,8 @@ static unsigned long modeset_get_crtc_power_domains(struct drm_crtc *crtc) unsigned long domains, new_domains, old_domains; old_domains = intel_crtc->enabled_power_domains; - intel_crtc->enabled_power_domains = new_domains = get_crtc_power_domains(crtc); + intel_crtc->enabled_power_domains = new_domains = + get_crtc_power_domains(crtc, crtc_state); domains = new_domains & ~old_domains; @@ -5354,31 +5363,6 @@ static void modeset_put_power_domains(struct drm_i915_private *dev_priv, intel_display_power_put(dev_priv, domain); } -static void modeset_update_crtc_power_domains(struct drm_atomic_state *state) -{ - struct intel_atomic_state *intel_state = to_intel_atomic_state(state); - struct drm_device *dev = state->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - unsigned long put_domains[I915_MAX_PIPES] = {}; - struct drm_crtc_state *crtc_state; - struct drm_crtc *crtc; - int i; - - for_each_crtc_in_state(state, crtc, crtc_state, i) { - if (needs_modeset(crtc->state)) - put_domains[to_intel_crtc(crtc)->pipe] = - modeset_get_crtc_power_domains(crtc); - } - - if (dev_priv->display.modeset_commit_cdclk && - intel_state->dev_cdclk != dev_priv->cdclk_freq) - dev_priv->display.modeset_commit_cdclk(state); - - for (i = 0; i < I915_MAX_PIPES; i++) - if (put_domains[i]) - modeset_put_power_domains(dev_priv, put_domains[i]); -} - static int intel_compute_max_dotclk(struct drm_i915_private *dev_priv) { int max_cdclk_freq = dev_priv->max_cdclk_freq; @@ -6041,8 +6025,7 @@ static int broxton_calc_cdclk(struct drm_i915_private *dev_priv, return 144000; } -/* Compute the max pixel clock for new configuration. Uses atomic state if - * that's non-NULL, look at current state otherwise. */ +/* Compute the max pixel clock for new configuration. */ static int intel_mode_max_pixclk(struct drm_device *dev, struct drm_atomic_state *state) { @@ -6065,9 +6048,6 @@ static int intel_mode_max_pixclk(struct drm_device *dev, intel_state->min_pixclk[i] = pixclk; } - if (!intel_state->active_crtcs) - return 0; - for_each_pipe(dev_priv, pipe) max_pixclk = max(intel_state->min_pixclk[pipe], max_pixclk); @@ -6291,8 +6271,6 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) for_each_encoder_on_crtc(dev, crtc, encoder) encoder->enable(encoder); - - intel_fbc_enable(intel_crtc); } static void i9xx_pfit_disable(struct intel_crtc *crtc) @@ -6355,8 +6333,6 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) if (!IS_GEN2(dev)) intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false); - - intel_fbc_disable_crtc(intel_crtc); } static void intel_crtc_disable_noatomic(struct drm_crtc *crtc) @@ -6380,6 +6356,7 @@ static void intel_crtc_disable_noatomic(struct drm_crtc *crtc) dev_priv->display.crtc_disable(crtc); intel_crtc->active = false; + intel_fbc_disable(intel_crtc); intel_update_watermarks(crtc); intel_disable_shared_dpll(intel_crtc); @@ -6398,55 +6375,16 @@ static void intel_crtc_disable_noatomic(struct drm_crtc *crtc) */ int intel_display_suspend(struct drm_device *dev) { - struct drm_mode_config *config = &dev->mode_config; - struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx; + struct drm_i915_private *dev_priv = to_i915(dev); struct drm_atomic_state *state; - struct drm_crtc *crtc; - unsigned crtc_mask = 0; - int ret = 0; - - if (WARN_ON(!ctx)) - return 0; - - lockdep_assert_held(&ctx->ww_ctx); - state = drm_atomic_state_alloc(dev); - if (WARN_ON(!state)) - return -ENOMEM; - - state->acquire_ctx = ctx; - state->allow_modeset = true; - - for_each_crtc(dev, crtc) { - struct drm_crtc_state *crtc_state = - drm_atomic_get_crtc_state(state, crtc); - - ret = PTR_ERR_OR_ZERO(crtc_state); - if (ret) - goto free; - - if (!crtc_state->active) - continue; - - crtc_state->active = false; - crtc_mask |= 1 << drm_crtc_index(crtc); - } - - if (crtc_mask) { - ret = drm_atomic_commit(state); - - if (!ret) { - for_each_crtc(dev, crtc) - if (crtc_mask & (1 << drm_crtc_index(crtc))) - crtc->state->active = true; - - return ret; - } - } + int ret; -free: + state = drm_atomic_helper_suspend(dev); + ret = PTR_ERR_OR_ZERO(state); if (ret) DRM_ERROR("Suspending crtc's failed with %i\n", ret); - drm_atomic_state_free(state); + else + dev_priv->modeset_restore_state = state; return ret; } @@ -8186,18 +8124,22 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc, { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + enum intel_display_power_domain power_domain; uint32_t tmp; + bool ret; - if (!intel_display_power_is_enabled(dev_priv, - POWER_DOMAIN_PIPE(crtc->pipe))) + power_domain = POWER_DOMAIN_PIPE(crtc->pipe); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) return false; pipe_config->cpu_transcoder = (enum transcoder) crtc->pipe; pipe_config->shared_dpll = DPLL_ID_PRIVATE; + ret = false; + tmp = I915_READ(PIPECONF(crtc->pipe)); if (!(tmp & PIPECONF_ENABLE)) - return false; + goto out; if (IS_G4X(dev) || IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) { switch (tmp & PIPECONF_BPC_MASK) { @@ -8277,7 +8219,12 @@ static bool i9xx_get_pipe_config(struct intel_crtc *crtc, pipe_config->base.adjusted_mode.crtc_clock = pipe_config->port_clock / pipe_config->pixel_multiplier; - return true; + ret = true; + +out: + intel_display_power_put(dev_priv, power_domain); + + return ret; } static void ironlake_init_pch_refclk(struct drm_device *dev) @@ -9381,18 +9328,21 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + enum intel_display_power_domain power_domain; uint32_t tmp; + bool ret; - if (!intel_display_power_is_enabled(dev_priv, - POWER_DOMAIN_PIPE(crtc->pipe))) + power_domain = POWER_DOMAIN_PIPE(crtc->pipe); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) return false; pipe_config->cpu_transcoder = (enum transcoder) crtc->pipe; pipe_config->shared_dpll = DPLL_ID_PRIVATE; + ret = false; tmp = I915_READ(PIPECONF(crtc->pipe)); if (!(tmp & PIPECONF_ENABLE)) - return false; + goto out; switch (tmp & PIPECONF_BPC_MASK) { case PIPECONF_6BPC: @@ -9455,7 +9405,12 @@ static bool ironlake_get_pipe_config(struct intel_crtc *crtc, ironlake_get_pfit_config(crtc, pipe_config); - return true; + ret = true; + +out: + intel_display_power_put(dev_priv, power_domain); + + return ret; } static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv) @@ -9721,9 +9676,6 @@ static int ilk_max_pixel_rate(struct drm_atomic_state *state) intel_state->min_pixclk[i] = pixel_rate; } - if (!intel_state->active_crtcs) - return 0; - for_each_pipe(dev_priv, pipe) max_pixel_rate = max(intel_state->min_pixclk[pipe], max_pixel_rate); @@ -9853,8 +9805,13 @@ static void broadwell_modeset_commit_cdclk(struct drm_atomic_state *old_state) static int haswell_crtc_compute_clock(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state) { - if (!intel_ddi_pll_select(crtc, crtc_state)) - return -EINVAL; + struct intel_encoder *intel_encoder = + intel_ddi_get_crtc_new_encoder(crtc_state); + + if (intel_encoder->type != INTEL_OUTPUT_DSI) { + if (!intel_ddi_pll_select(crtc, crtc_state)) + return -EINVAL; + } crtc->lowfreq_avail = false; @@ -9982,12 +9939,17 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, { struct drm_device *dev = crtc->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - enum intel_display_power_domain pfit_domain; + enum intel_display_power_domain power_domain; + unsigned long power_domain_mask; uint32_t tmp; + bool ret; - if (!intel_display_power_is_enabled(dev_priv, - POWER_DOMAIN_PIPE(crtc->pipe))) + power_domain = POWER_DOMAIN_PIPE(crtc->pipe); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) return false; + power_domain_mask = BIT(power_domain); + + ret = false; pipe_config->cpu_transcoder = (enum transcoder) crtc->pipe; pipe_config->shared_dpll = DPLL_ID_PRIVATE; @@ -10014,13 +9976,14 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, pipe_config->cpu_transcoder = TRANSCODER_EDP; } - if (!intel_display_power_is_enabled(dev_priv, - POWER_DOMAIN_TRANSCODER(pipe_config->cpu_transcoder))) - return false; + power_domain = POWER_DOMAIN_TRANSCODER(pipe_config->cpu_transcoder); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) + goto out; + power_domain_mask |= BIT(power_domain); tmp = I915_READ(PIPECONF(pipe_config->cpu_transcoder)); if (!(tmp & PIPECONF_ENABLE)) - return false; + goto out; haswell_get_ddi_port_state(crtc, pipe_config); @@ -10030,14 +9993,14 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, skl_init_scalers(dev, crtc, pipe_config); } - pfit_domain = POWER_DOMAIN_PIPE_PANEL_FITTER(crtc->pipe); - if (INTEL_INFO(dev)->gen >= 9) { pipe_config->scaler_state.scaler_id = -1; pipe_config->scaler_state.scaler_users &= ~(1 << SKL_CRTC_INDEX); } - if (intel_display_power_is_enabled(dev_priv, pfit_domain)) { + power_domain = POWER_DOMAIN_PIPE_PANEL_FITTER(crtc->pipe); + if (intel_display_power_get_if_enabled(dev_priv, power_domain)) { + power_domain_mask |= BIT(power_domain); if (INTEL_INFO(dev)->gen >= 9) skylake_get_pfit_config(crtc, pipe_config); else @@ -10055,7 +10018,13 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, pipe_config->pixel_multiplier = 1; } - return true; + ret = true; + +out: + for_each_power_domain(power_domain, power_domain_mask) + intel_display_power_put(dev_priv, power_domain); + + return ret; } static void i845_update_cursor(struct drm_crtc *crtc, u32 base, @@ -10376,6 +10345,7 @@ mode_fits_in_fbdev(struct drm_device *dev, if (obj->base.size < mode->vdisplay * fb->pitches[0]) return NULL; + drm_framebuffer_reference(fb); return fb; #else return NULL; @@ -10431,7 +10401,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, struct drm_device *dev = encoder->dev; struct drm_framebuffer *fb; struct drm_mode_config *config = &dev->mode_config; - struct drm_atomic_state *state = NULL; + struct drm_atomic_state *state = NULL, *restore_state = NULL; struct drm_connector_state *connector_state; struct intel_crtc_state *crtc_state; int ret, i = -1; @@ -10440,6 +10410,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, connector->base.id, connector->name, encoder->base.id, encoder->name); + old->restore_state = NULL; + retry: ret = drm_modeset_lock(&config->connection_mutex, ctx); if (ret) @@ -10456,24 +10428,15 @@ retry: */ /* See if we already have a CRTC for this connector */ - if (encoder->crtc) { - crtc = encoder->crtc; + if (connector->state->crtc) { + crtc = connector->state->crtc; ret = drm_modeset_lock(&crtc->mutex, ctx); if (ret) goto fail; - ret = drm_modeset_lock(&crtc->primary->mutex, ctx); - if (ret) - goto fail; - - old->dpms_mode = connector->dpms; - old->load_detect_temp = false; /* Make sure the crtc and connector are running */ - if (connector->dpms != DRM_MODE_DPMS_ON) - connector->funcs->dpms(connector, DRM_MODE_DPMS_ON); - - return true; + goto found; } /* Find an unused one (if possible) */ @@ -10481,8 +10444,15 @@ retry: i++; if (!(encoder->possible_crtcs & (1 << i))) continue; - if (possible_crtc->state->enable) + + ret = drm_modeset_lock(&possible_crtc->mutex, ctx); + if (ret) + goto fail; + + if (possible_crtc->state->enable) { + drm_modeset_unlock(&possible_crtc->mutex); continue; + } crtc = possible_crtc; break; @@ -10496,23 +10466,22 @@ retry: goto fail; } - ret = drm_modeset_lock(&crtc->mutex, ctx); - if (ret) - goto fail; +found: + intel_crtc = to_intel_crtc(crtc); + ret = drm_modeset_lock(&crtc->primary->mutex, ctx); if (ret) goto fail; - intel_crtc = to_intel_crtc(crtc); - old->dpms_mode = connector->dpms; - old->load_detect_temp = true; - old->release_fb = NULL; - state = drm_atomic_state_alloc(dev); - if (!state) - return false; + restore_state = drm_atomic_state_alloc(dev); + if (!state || !restore_state) { + ret = -ENOMEM; + goto fail; + } state->acquire_ctx = ctx; + restore_state->acquire_ctx = ctx; connector_state = drm_atomic_get_connector_state(state, connector); if (IS_ERR(connector_state)) { @@ -10520,7 +10489,9 @@ retry: goto fail; } - connector_state->crtc = crtc; + ret = drm_atomic_set_crtc_for_connector(connector_state, crtc); + if (ret) + goto fail; crtc_state = intel_atomic_get_crtc_state(state, intel_crtc); if (IS_ERR(crtc_state)) { @@ -10544,7 +10515,6 @@ retry: if (fb == NULL) { DRM_DEBUG_KMS("creating tmp fb for load-detection\n"); fb = intel_framebuffer_create_for_mode(dev, mode, 24, 32); - old->release_fb = fb; } else DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n"); if (IS_ERR(fb)) { @@ -10556,15 +10526,28 @@ retry: if (ret) goto fail; - drm_mode_copy(&crtc_state->base.mode, mode); + drm_framebuffer_unreference(fb); + + ret = drm_atomic_set_mode_for_crtc(&crtc_state->base, mode); + if (ret) + goto fail; + + ret = PTR_ERR_OR_ZERO(drm_atomic_get_connector_state(restore_state, connector)); + if (!ret) + ret = PTR_ERR_OR_ZERO(drm_atomic_get_crtc_state(restore_state, crtc)); + if (!ret) + ret = PTR_ERR_OR_ZERO(drm_atomic_get_plane_state(restore_state, crtc->primary)); + if (ret) { + DRM_DEBUG_KMS("Failed to create a copy of old state to restore: %i\n", ret); + goto fail; + } if (drm_atomic_commit(state)) { DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n"); - if (old->release_fb) - old->release_fb->funcs->destroy(old->release_fb); goto fail; } - crtc->primary->crtc = crtc; + + old->restore_state = restore_state; /* let the connector get through one full cycle before testing */ intel_wait_for_vblank(dev, intel_crtc->pipe); @@ -10572,7 +10555,8 @@ retry: fail: drm_atomic_state_free(state); - state = NULL; + drm_atomic_state_free(restore_state); + restore_state = state = NULL; if (ret == -EDEADLK) { drm_modeset_backoff(ctx); @@ -10586,65 +10570,24 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, struct intel_load_detect_pipe *old, struct drm_modeset_acquire_ctx *ctx) { - struct drm_device *dev = connector->dev; struct intel_encoder *intel_encoder = intel_attached_encoder(connector); struct drm_encoder *encoder = &intel_encoder->base; - struct drm_crtc *crtc = encoder->crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct drm_atomic_state *state; - struct drm_connector_state *connector_state; - struct intel_crtc_state *crtc_state; + struct drm_atomic_state *state = old->restore_state; int ret; DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", connector->base.id, connector->name, encoder->base.id, encoder->name); - if (old->load_detect_temp) { - state = drm_atomic_state_alloc(dev); - if (!state) - goto fail; - - state->acquire_ctx = ctx; - - connector_state = drm_atomic_get_connector_state(state, connector); - if (IS_ERR(connector_state)) - goto fail; - - crtc_state = intel_atomic_get_crtc_state(state, intel_crtc); - if (IS_ERR(crtc_state)) - goto fail; - - connector_state->crtc = NULL; - - crtc_state->base.enable = crtc_state->base.active = false; - - ret = intel_modeset_setup_plane_state(state, crtc, NULL, NULL, - 0, 0); - if (ret) - goto fail; - - ret = drm_atomic_commit(state); - if (ret) - goto fail; - - if (old->release_fb) { - drm_framebuffer_unregister_private(old->release_fb); - drm_framebuffer_unreference(old->release_fb); - } - + if (!state) return; - } - - /* Switch crtc and encoder back off if necessary */ - if (old->dpms_mode != DRM_MODE_DPMS_ON) - connector->funcs->dpms(connector, old->dpms_mode); - return; -fail: - DRM_DEBUG_KMS("Couldn't release load detect pipe.\n"); - drm_atomic_state_free(state); + ret = drm_atomic_commit(state); + if (ret) { + DRM_DEBUG_KMS("Couldn't release load detect pipe: %i\n", ret); + drm_atomic_state_free(state); + } } static int i9xx_pll_refclk(struct drm_device *dev, @@ -10914,6 +10857,7 @@ static void intel_unpin_work_fn(struct work_struct *__work) mutex_unlock(&dev->struct_mutex); intel_frontbuffer_flip_complete(dev, to_intel_plane(primary)->frontbuffer_bit); + intel_fbc_post_update(crtc); drm_framebuffer_unreference(work->old_fb); BUG_ON(atomic_read(&crtc->unpin_work_count) == 0); @@ -10995,6 +10939,12 @@ static bool page_flip_finished(struct intel_crtc *crtc) return true; /* + * BDW signals flip done immediately if the plane + * is disabled, even if the plane enable is already + * armed to occur at the next vblank :( + */ + + /* * A DSPSURFLIVE check isn't enough in case the mmio and CS flips * used the same base address. In that case the mmio flip might * have completed, but the CS hasn't even executed the flip yet. @@ -11629,6 +11579,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, crtc->primary->fb = fb; update_state_fb(crtc->primary); + intel_fbc_pre_update(intel_crtc); work->pending_flip_obj = obj; @@ -11713,7 +11664,6 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, to_intel_plane(primary)->frontbuffer_bit); mutex_unlock(&dev->struct_mutex); - intel_fbc_deactivate(intel_crtc); intel_frontbuffer_flip_prepare(dev, to_intel_plane(primary)->frontbuffer_bit); @@ -11724,7 +11674,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, cleanup_unpin: intel_unpin_fb_obj(fb, crtc->primary->state); cleanup_pending: - if (request) + if (!IS_ERR_OR_NULL(request)) i915_gem_request_cancel(request); atomic_dec(&intel_crtc->unpin_work_count); mutex_unlock(&dev->struct_mutex); @@ -11835,11 +11785,9 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct drm_plane *plane = plane_state->plane; struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_plane_state *old_plane_state = to_intel_plane_state(plane->state); int idx = intel_crtc->base.base.id, ret; - int i = drm_plane_index(plane); bool mode_changed = needs_modeset(crtc_state); bool was_crtc_enabled = crtc->state->active; bool is_crtc_enabled = crtc_state->active; @@ -11872,6 +11820,9 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state, if (!was_visible && !visible) return 0; + if (fb != old_plane_state->base.fb) + pipe_config->fb_changed = true; + turn_off = was_visible && (!visible || mode_changed); turn_on = visible && (!was_visible || mode_changed); @@ -11886,11 +11837,8 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state, pipe_config->wm_changed = true; /* must disable cxsr around plane enable/disable */ - if (plane->type != DRM_PLANE_TYPE_CURSOR) { - if (is_crtc_enabled) - intel_crtc->atomic.wait_vblank = true; + if (plane->type != DRM_PLANE_TYPE_CURSOR) pipe_config->disable_cxsr = true; - } } else if (intel_wm_need_update(plane, plane_state)) { pipe_config->wm_changed = true; } @@ -11901,49 +11849,9 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state, switch (plane->type) { case DRM_PLANE_TYPE_PRIMARY: - intel_crtc->atomic.pre_disable_primary = turn_off; intel_crtc->atomic.post_enable_primary = turn_on; + intel_crtc->atomic.update_fbc = true; - if (turn_off) { - /* - * FIXME: Actually if we will still have any other - * plane enabled on the pipe we could let IPS enabled - * still, but for now lets consider that when we make - * primary invisible by setting DSPCNTR to 0 on - * update_primary_plane function IPS needs to be - * disable. - */ - intel_crtc->atomic.disable_ips = true; - - intel_crtc->atomic.disable_fbc = true; - } - - /* - * FBC does not work on some platforms for rotated - * planes, so disable it when rotation is not 0 and - * update it when rotation is set back to 0. - * - * FIXME: This is redundant with the fbc update done in - * the primary plane enable function except that that - * one is done too late. We eventually need to unify - * this. - */ - - if (visible && - INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev) && - dev_priv->fbc.crtc == intel_crtc && - plane_state->rotation != BIT(DRM_ROTATE_0)) - intel_crtc->atomic.disable_fbc = true; - - /* - * BDW signals flip done immediately if the plane - * is disabled, even if the plane enable is already - * armed to occur at the next vblank :( - */ - if (turn_on && IS_BROADWELL(dev)) - intel_crtc->atomic.wait_vblank = true; - - intel_crtc->atomic.update_fbc |= visible || mode_changed; break; case DRM_PLANE_TYPE_CURSOR: break; @@ -11956,13 +11864,8 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state, */ if (IS_IVYBRIDGE(dev) && needs_scaling(to_intel_plane_state(plane_state)) && - !needs_scaling(old_plane_state)) { - to_intel_crtc_state(crtc_state)->disable_lp_wm = true; - } else if (turn_off && !mode_changed) { - intel_crtc->atomic.wait_vblank = true; - intel_crtc->atomic.update_sprite_watermarks |= - 1 << i; - } + !needs_scaling(old_plane_state)) + pipe_config->disable_lp_wm = true; break; } @@ -13130,8 +13033,6 @@ static void intel_modeset_clear_plls(struct drm_atomic_state *state) struct drm_device *dev = state->dev; struct drm_i915_private *dev_priv = to_i915(dev); struct intel_shared_dpll_config *shared_dpll = NULL; - struct intel_crtc *intel_crtc; - struct intel_crtc_state *intel_crtc_state; struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; int i; @@ -13140,21 +13041,21 @@ static void intel_modeset_clear_plls(struct drm_atomic_state *state) return; for_each_crtc_in_state(state, crtc, crtc_state, i) { - int dpll; - - intel_crtc = to_intel_crtc(crtc); - intel_crtc_state = to_intel_crtc_state(crtc_state); - dpll = intel_crtc_state->shared_dpll; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int old_dpll = to_intel_crtc_state(crtc->state)->shared_dpll; - if (!needs_modeset(crtc_state) || dpll == DPLL_ID_PRIVATE) + if (!needs_modeset(crtc_state)) continue; - intel_crtc_state->shared_dpll = DPLL_ID_PRIVATE; + to_intel_crtc_state(crtc_state)->shared_dpll = DPLL_ID_PRIVATE; + + if (old_dpll == DPLL_ID_PRIVATE) + continue; if (!shared_dpll) shared_dpll = intel_atomic_get_shared_dpll_state(state); - shared_dpll[dpll].crtc_mask &= ~(1 << intel_crtc->pipe); + shared_dpll[old_dpll].crtc_mask &= ~(1 << intel_crtc->pipe); } } @@ -13290,6 +13191,9 @@ static int intel_modeset_checks(struct drm_atomic_state *state) if (ret < 0) return ret; + + DRM_DEBUG_KMS("New cdclk calculated to be atomic %u, actual %u\n", + intel_state->cdclk, intel_state->dev_cdclk); } else to_intel_atomic_state(state)->cdclk = dev_priv->atomic_cdclk_freq; @@ -13348,6 +13252,7 @@ static void calc_watermark_data(struct drm_atomic_state *state) static int intel_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { + struct drm_i915_private *dev_priv = to_i915(dev); struct intel_atomic_state *intel_state = to_intel_atomic_state(state); struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; @@ -13390,7 +13295,7 @@ static int intel_atomic_check(struct drm_device *dev, return ret; if (i915.fastboot && - intel_pipe_config_compare(state->dev, + intel_pipe_config_compare(dev, to_intel_crtc_state(crtc->state), pipe_config, true)) { crtc_state->mode_changed = false; @@ -13416,12 +13321,13 @@ static int intel_atomic_check(struct drm_device *dev, if (ret) return ret; } else - intel_state->cdclk = to_i915(state->dev)->cdclk_freq; + intel_state->cdclk = dev_priv->cdclk_freq; - ret = drm_atomic_helper_check_planes(state->dev, state); + ret = drm_atomic_helper_check_planes(dev, state); if (ret) return ret; + intel_fbc_choose_crtc(dev_priv, state); calc_watermark_data(state); return 0; @@ -13493,6 +13399,71 @@ static int intel_atomic_prepare_commit(struct drm_device *dev, return ret; } +static void intel_atomic_wait_for_vblanks(struct drm_device *dev, + struct drm_i915_private *dev_priv, + unsigned crtc_mask) +{ + unsigned last_vblank_count[I915_MAX_PIPES]; + enum pipe pipe; + int ret; + + if (!crtc_mask) + return; + + for_each_pipe(dev_priv, pipe) { + struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; + + if (!((1 << pipe) & crtc_mask)) + continue; + + ret = drm_crtc_vblank_get(crtc); + if (WARN_ON(ret != 0)) { + crtc_mask &= ~(1 << pipe); + continue; + } + + last_vblank_count[pipe] = drm_crtc_vblank_count(crtc); + } + + for_each_pipe(dev_priv, pipe) { + struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe]; + long lret; + + if (!((1 << pipe) & crtc_mask)) + continue; + + lret = wait_event_timeout(dev->vblank[pipe].queue, + last_vblank_count[pipe] != + drm_crtc_vblank_count(crtc), + msecs_to_jiffies(50)); + + WARN_ON(!lret); + + drm_crtc_vblank_put(crtc); + } +} + +static bool needs_vblank_wait(struct intel_crtc_state *crtc_state) +{ + /* fb updated, need to unpin old fb */ + if (crtc_state->fb_changed) + return true; + + /* wm changes, need vblank before final wm's */ + if (crtc_state->wm_changed) + return true; + + /* + * cxsr is re-enabled after vblank. + * This is already handled by crtc_state->wm_changed, + * but added for clarity. + */ + if (crtc_state->disable_cxsr) + return true; + + return false; +} + /** * intel_atomic_commit - commit validated state object * @dev: DRM device @@ -13519,6 +13490,8 @@ static int intel_atomic_commit(struct drm_device *dev, struct drm_crtc *crtc; int ret = 0, i; bool hw_check = intel_state->modeset; + unsigned long put_domains[I915_MAX_PIPES] = {}; + unsigned crtc_vblank_mask = 0; ret = intel_atomic_prepare_commit(dev, state, async); if (ret) { @@ -13534,20 +13507,32 @@ static int intel_atomic_commit(struct drm_device *dev, sizeof(intel_state->min_pixclk)); dev_priv->active_crtcs = intel_state->active_crtcs; dev_priv->atomic_cdclk_freq = intel_state->cdclk; + + intel_display_power_get(dev_priv, POWER_DOMAIN_MODESET); } for_each_crtc_in_state(state, crtc, crtc_state, i) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + if (needs_modeset(crtc->state) || + to_intel_crtc_state(crtc->state)->update_pipe) { + hw_check = true; + + put_domains[to_intel_crtc(crtc)->pipe] = + modeset_get_crtc_power_domains(crtc, + to_intel_crtc_state(crtc->state)); + } + if (!needs_modeset(crtc->state)) continue; - intel_pre_plane_update(intel_crtc); + intel_pre_plane_update(to_intel_crtc_state(crtc_state)); if (crtc_state->active) { intel_crtc_disable_planes(crtc, crtc_state->plane_mask); dev_priv->display.crtc_disable(crtc); intel_crtc->active = false; + intel_fbc_disable(intel_crtc); intel_disable_shared_dpll(intel_crtc); /* @@ -13570,51 +13555,53 @@ static int intel_atomic_commit(struct drm_device *dev, intel_shared_dpll_commit(state); drm_atomic_helper_update_legacy_modeset_state(state->dev, state); - modeset_update_crtc_power_domains(state); + + if (dev_priv->display.modeset_commit_cdclk && + intel_state->dev_cdclk != dev_priv->cdclk_freq) + dev_priv->display.modeset_commit_cdclk(state); } /* Now enable the clocks, plane, pipe, and connectors that we set up. */ for_each_crtc_in_state(state, crtc, crtc_state, i) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); bool modeset = needs_modeset(crtc->state); - bool update_pipe = !modeset && - to_intel_crtc_state(crtc->state)->update_pipe; - unsigned long put_domains = 0; - - if (modeset) - intel_display_power_get(dev_priv, POWER_DOMAIN_MODESET); + struct intel_crtc_state *pipe_config = + to_intel_crtc_state(crtc->state); + bool update_pipe = !modeset && pipe_config->update_pipe; if (modeset && crtc->state->active) { update_scanline_offset(to_intel_crtc(crtc)); dev_priv->display.crtc_enable(crtc); } - if (update_pipe) { - put_domains = modeset_get_crtc_power_domains(crtc); - - /* make sure intel_modeset_check_state runs */ - hw_check = true; - } - if (!modeset) - intel_pre_plane_update(intel_crtc); + intel_pre_plane_update(to_intel_crtc_state(crtc_state)); + + if (crtc->state->active && intel_crtc->atomic.update_fbc) + intel_fbc_enable(intel_crtc); if (crtc->state->active && (crtc->state->planes_changed || update_pipe)) drm_atomic_helper_commit_planes_on_crtc(crtc_state); - if (put_domains) - modeset_put_power_domains(dev_priv, put_domains); - - intel_post_plane_update(intel_crtc); - - if (modeset) - intel_display_power_put(dev_priv, POWER_DOMAIN_MODESET); + if (pipe_config->base.active && needs_vblank_wait(pipe_config)) + crtc_vblank_mask |= 1 << i; } /* FIXME: add subpixel order */ - drm_atomic_helper_wait_for_vblanks(dev, state); + if (!state->legacy_cursor_update) + intel_atomic_wait_for_vblanks(dev, dev_priv, crtc_vblank_mask); + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + intel_post_plane_update(to_intel_crtc(crtc)); + + if (put_domains[i]) + modeset_put_power_domains(dev_priv, put_domains[i]); + } + + if (intel_state->modeset) + intel_display_power_put(dev_priv, POWER_DOMAIN_MODESET); mutex_lock(&dev->struct_mutex); drm_atomic_helper_cleanup_planes(dev, state); @@ -13696,7 +13683,7 @@ static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv, { uint32_t val; - if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PLLS)) + if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_PLLS)) return false; val = I915_READ(PCH_DPLL(pll->id)); @@ -13704,6 +13691,8 @@ static bool ibx_pch_dpll_get_hw_state(struct drm_i915_private *dev_priv, hw_state->fp0 = I915_READ(PCH_FP0(pll->id)); hw_state->fp1 = I915_READ(PCH_FP1(pll->id)); + intel_display_power_put(dev_priv, POWER_DOMAIN_PLLS); + return val & DPLL_VCO_ENABLE; } @@ -14682,10 +14671,12 @@ u32 intel_fb_pitch_limit(struct drm_device *dev, uint64_t fb_modifier, u32 gen = INTEL_INFO(dev)->gen; if (gen >= 9) { + int cpp = drm_format_plane_cpp(pixel_format, 0); + /* "The stride in bytes must not exceed the of the size of 8K * pixels and 32K bytes." */ - return min(8192*drm_format_plane_cpp(pixel_format, 0), 32768); + return min(8192 * cpp, 32768); } else if (gen >= 5 && !IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev)) { return 32*1024; } else if (gen >= 4) { @@ -15517,6 +15508,17 @@ static bool intel_crtc_has_encoders(struct intel_crtc *crtc) return false; } +static bool intel_encoder_has_connectors(struct intel_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct intel_connector *connector; + + for_each_connector_on_encoder(dev, &encoder->base, connector) + return true; + + return false; +} + static void intel_sanitize_crtc(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; @@ -15627,7 +15629,6 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder) { struct intel_connector *connector; struct drm_device *dev = encoder->base.dev; - bool active = false; /* We need to check both for a crtc link (meaning that the * encoder is active and trying to read from a pipe) and the @@ -15635,15 +15636,7 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder) bool has_active_crtc = encoder->base.crtc && to_intel_crtc(encoder->base.crtc)->active; - for_each_intel_connector(dev, connector) { - if (connector->base.encoder != &encoder->base) - continue; - - active = true; - break; - } - - if (active && !has_active_crtc) { + if (intel_encoder_has_connectors(encoder) && !has_active_crtc) { DRM_DEBUG_KMS("[ENCODER:%d:%s] has active connectors but no active pipe!\n", encoder->base.base.id, encoder->base.name); @@ -15698,10 +15691,12 @@ void i915_redisable_vga(struct drm_device *dev) * level, just check if the power well is enabled instead of trying to * follow the "don't touch the power well if we don't need it" policy * the rest of the driver uses. */ - if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_VGA)) + if (!intel_display_power_get_if_enabled(dev_priv, POWER_DOMAIN_VGA)) return; i915_redisable_vga_power_on(dev); + + intel_display_power_put(dev_priv, POWER_DOMAIN_VGA); } static bool primary_get_hw_state(struct intel_plane *plane) @@ -15929,63 +15924,76 @@ intel_modeset_setup_hw_state(struct drm_device *dev) for_each_intel_crtc(dev, crtc) { unsigned long put_domains; - put_domains = modeset_get_crtc_power_domains(&crtc->base); + put_domains = modeset_get_crtc_power_domains(&crtc->base, crtc->config); if (WARN_ON(put_domains)) modeset_put_power_domains(dev_priv, put_domains); } intel_display_set_init_power(dev_priv, false); + + intel_fbc_init_pipe_state(dev_priv); } void intel_display_resume(struct drm_device *dev) { - struct drm_atomic_state *state = drm_atomic_state_alloc(dev); - struct intel_connector *conn; - struct intel_plane *plane; - struct drm_crtc *crtc; + struct drm_i915_private *dev_priv = to_i915(dev); + struct drm_atomic_state *state = dev_priv->modeset_restore_state; + struct drm_modeset_acquire_ctx ctx; int ret; + bool setup = false; - if (!state) - return; - - state->acquire_ctx = dev->mode_config.acquire_ctx; + dev_priv->modeset_restore_state = NULL; - /* preserve complete old state, including dpll */ - intel_atomic_get_shared_dpll_state(state); + /* + * This is a cludge because with real atomic modeset mode_config.mutex + * won't be taken. Unfortunately some probed state like + * audio_codec_enable is still protected by mode_config.mutex, so lock + * it here for now. + */ + mutex_lock(&dev->mode_config.mutex); + drm_modeset_acquire_init(&ctx, 0); - for_each_crtc(dev, crtc) { - struct drm_crtc_state *crtc_state = - drm_atomic_get_crtc_state(state, crtc); +retry: + ret = drm_modeset_lock_all_ctx(dev, &ctx); - ret = PTR_ERR_OR_ZERO(crtc_state); - if (ret) - goto err; + if (ret == 0 && !setup) { + setup = true; - /* force a restore */ - crtc_state->mode_changed = true; + intel_modeset_setup_hw_state(dev); + i915_redisable_vga(dev); } - for_each_intel_plane(dev, plane) { - ret = PTR_ERR_OR_ZERO(drm_atomic_get_plane_state(state, &plane->base)); - if (ret) - goto err; - } + if (ret == 0 && state) { + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + int i; - for_each_intel_connector(dev, conn) { - ret = PTR_ERR_OR_ZERO(drm_atomic_get_connector_state(state, &conn->base)); - if (ret) - goto err; + state->acquire_ctx = &ctx; + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + /* + * Force recalculation even if we restore + * current state. With fast modeset this may not result + * in a modeset when the state is compatible. + */ + crtc_state->mode_changed = true; + } + + ret = drm_atomic_commit(state); } - intel_modeset_setup_hw_state(dev); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } - i915_redisable_vga(dev); - ret = drm_atomic_commit(state); - if (!ret) - return; + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + mutex_unlock(&dev->mode_config.mutex); -err: - DRM_ERROR("Restoring old state failed with %i\n", ret); - drm_atomic_state_free(state); + if (ret) { + DRM_ERROR("Restoring old state failed with %i\n", ret); + drm_atomic_state_free(state); + } } void intel_modeset_gem_init(struct drm_device *dev) @@ -15994,9 +16002,7 @@ void intel_modeset_gem_init(struct drm_device *dev) struct drm_i915_gem_object *obj; int ret; - mutex_lock(&dev->struct_mutex); intel_init_gt_powersave(dev); - mutex_unlock(&dev->struct_mutex); intel_modeset_init_hw(dev); @@ -16063,7 +16069,7 @@ void intel_modeset_cleanup(struct drm_device *dev) intel_unregister_dsm_handler(); - intel_fbc_disable(dev_priv); + intel_fbc_global_disable(dev_priv); /* flush any delayed tasks or pending work */ flush_scheduled_work(); @@ -16076,9 +16082,7 @@ void intel_modeset_cleanup(struct drm_device *dev) intel_cleanup_overlay(dev); - mutex_lock(&dev->struct_mutex); intel_cleanup_gt_powersave(dev); - mutex_unlock(&dev->struct_mutex); intel_teardown_gmbus(dev); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index da704c6ee5cb..cbc06596659a 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -203,6 +203,7 @@ intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; int target_clock = mode->clock; int max_rate, mode_rate, max_lanes, max_link_clock; + int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; if (is_edp(intel_dp) && fixed_mode) { if (mode->hdisplay > fixed_mode->hdisplay) @@ -220,7 +221,7 @@ intel_dp_mode_valid(struct drm_connector *connector, max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); mode_rate = intel_dp_link_required(target_clock, 18); - if (mode_rate > max_rate) + if (mode_rate > max_rate || target_clock > max_dotclk) return MODE_CLOCK_HIGH; if (mode->clock < 10000) @@ -979,7 +980,10 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) if (WARN_ON(txsize > 20)) return -E2BIG; - memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size); + if (msg->buffer) + memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size); + else + WARN_ON(msg->size); ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize); if (ret > 0) { @@ -1798,12 +1802,21 @@ static void wait_panel_off(struct intel_dp *intel_dp) static void wait_panel_power_cycle(struct intel_dp *intel_dp) { + ktime_t panel_power_on_time; + s64 panel_power_off_duration; + DRM_DEBUG_KMS("Wait for panel power cycle\n"); + /* take the difference of currrent time and panel power off time + * and then make panel wait for t11_t12 if needed. */ + panel_power_on_time = ktime_get_boottime(); + panel_power_off_duration = ktime_ms_delta(panel_power_on_time, intel_dp->panel_power_off_time); + /* When we disable the VDD override bit last we have to do the manual * wait. */ - wait_remaining_ms_from_jiffies(intel_dp->last_power_cycle, - intel_dp->panel_power_cycle_delay); + if (panel_power_off_duration < (s64)intel_dp->panel_power_cycle_delay) + wait_remaining_ms_from_jiffies(jiffies, + intel_dp->panel_power_cycle_delay - panel_power_off_duration); wait_panel_status(intel_dp, IDLE_CYCLE_MASK, IDLE_CYCLE_VALUE); } @@ -1955,7 +1968,7 @@ static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp) I915_READ(pp_stat_reg), I915_READ(pp_ctrl_reg)); if ((pp & POWER_TARGET_ON) == 0) - intel_dp->last_power_cycle = jiffies; + intel_dp->panel_power_off_time = ktime_get_boottime(); power_domain = intel_display_port_aux_power_domain(intel_encoder); intel_display_power_put(dev_priv, power_domain); @@ -2104,7 +2117,7 @@ static void edp_panel_off(struct intel_dp *intel_dp) I915_WRITE(pp_ctrl_reg, pp); POSTING_READ(pp_ctrl_reg); - intel_dp->last_power_cycle = jiffies; + intel_dp->panel_power_off_time = ktime_get_boottime(); wait_panel_off(intel_dp); /* We got a reference when we enabled the VDD. */ @@ -2343,15 +2356,18 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder, struct drm_i915_private *dev_priv = dev->dev_private; enum intel_display_power_domain power_domain; u32 tmp; + bool ret; power_domain = intel_display_port_power_domain(encoder); - if (!intel_display_power_is_enabled(dev_priv, power_domain)) + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) return false; + ret = false; + tmp = I915_READ(intel_dp->output_reg); if (!(tmp & DP_PORT_EN)) - return false; + goto out; if (IS_GEN7(dev) && port == PORT_A) { *pipe = PORT_TO_PIPE_CPT(tmp); @@ -2362,7 +2378,9 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder, u32 trans_dp = I915_READ(TRANS_DP_CTL(p)); if (TRANS_DP_PIPE_TO_PORT(trans_dp) == port) { *pipe = p; - return true; + ret = true; + + goto out; } } @@ -2374,7 +2392,12 @@ static bool intel_dp_get_hw_state(struct intel_encoder *encoder, *pipe = PORT_TO_PIPE(tmp); } - return true; + ret = true; + +out: + intel_display_power_put(dev_priv, power_domain); + + return ret; } static void intel_dp_get_config(struct intel_encoder *encoder, @@ -3995,7 +4018,7 @@ static int intel_dp_sink_crc_stop(struct intel_dp *intel_dp) } while (--attempts && count); if (attempts == 0) { - DRM_ERROR("TIMEOUT: Sink CRC counter is not zeroed\n"); + DRM_DEBUG_KMS("TIMEOUT: Sink CRC counter is not zeroed after calculation is stopped\n"); ret = -ETIMEDOUT; } @@ -4474,20 +4497,20 @@ static bool g4x_digital_port_connected(struct drm_i915_private *dev_priv, return I915_READ(PORT_HOTPLUG_STAT) & bit; } -static bool vlv_digital_port_connected(struct drm_i915_private *dev_priv, - struct intel_digital_port *port) +static bool gm45_digital_port_connected(struct drm_i915_private *dev_priv, + struct intel_digital_port *port) { u32 bit; switch (port->port) { case PORT_B: - bit = PORTB_HOTPLUG_LIVE_STATUS_VLV; + bit = PORTB_HOTPLUG_LIVE_STATUS_GM45; break; case PORT_C: - bit = PORTC_HOTPLUG_LIVE_STATUS_VLV; + bit = PORTC_HOTPLUG_LIVE_STATUS_GM45; break; case PORT_D: - bit = PORTD_HOTPLUG_LIVE_STATUS_VLV; + bit = PORTD_HOTPLUG_LIVE_STATUS_GM45; break; default: MISSING_CASE(port->port); @@ -4535,12 +4558,12 @@ bool intel_digital_port_connected(struct drm_i915_private *dev_priv, { if (HAS_PCH_IBX(dev_priv)) return ibx_digital_port_connected(dev_priv, port); - if (HAS_PCH_SPLIT(dev_priv)) + else if (HAS_PCH_SPLIT(dev_priv)) return cpt_digital_port_connected(dev_priv, port); else if (IS_BROXTON(dev_priv)) return bxt_digital_port_connected(dev_priv, port); - else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) - return vlv_digital_port_connected(dev_priv, port); + else if (IS_GM45(dev_priv)) + return gm45_digital_port_connected(dev_priv, port); else return g4x_digital_port_connected(dev_priv, port); } @@ -5102,7 +5125,7 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect static void intel_dp_init_panel_power_timestamps(struct intel_dp *intel_dp) { - intel_dp->last_power_cycle = jiffies; + intel_dp->panel_power_off_time = ktime_get_boottime(); intel_dp->last_power_on = jiffies; intel_dp->last_backlight_off = jiffies; } diff --git a/drivers/gpu/drm/i915/intel_dp_link_training.c b/drivers/gpu/drm/i915/intel_dp_link_training.c index 88887938e0bf..0b8eefc2acc5 100644 --- a/drivers/gpu/drm/i915/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/intel_dp_link_training.c @@ -215,27 +215,46 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp) } } -static void -intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp) +/* + * Pick training pattern for channel equalization. Training Pattern 3 for HBR2 + * or 1.2 devices that support it, Training Pattern 2 otherwise. + */ +static u32 intel_dp_training_pattern(struct intel_dp *intel_dp) { - bool channel_eq = false; - int tries, cr_tries; - uint32_t training_pattern = DP_TRAINING_PATTERN_2; + u32 training_pattern = DP_TRAINING_PATTERN_2; + bool source_tps3, sink_tps3; /* - * Training Pattern 3 for HBR2 or 1.2 devices that support it. - * * Intel platforms that support HBR2 also support TPS3. TPS3 support is - * also mandatory for downstream devices that support HBR2. + * also mandatory for downstream devices that support HBR2. However, not + * all sinks follow the spec. * * Due to WaDisableHBR2 SKL < B0 is the only exception where TPS3 is - * supported but still not enabled. + * supported in source but still not enabled. */ - if (intel_dp_source_supports_hbr2(intel_dp) && - drm_dp_tps3_supported(intel_dp->dpcd)) + source_tps3 = intel_dp_source_supports_hbr2(intel_dp); + sink_tps3 = drm_dp_tps3_supported(intel_dp->dpcd); + + if (source_tps3 && sink_tps3) { training_pattern = DP_TRAINING_PATTERN_3; - else if (intel_dp->link_rate == 540000) - DRM_ERROR("5.4 Gbps link rate without HBR2/TPS3 support\n"); + } else if (intel_dp->link_rate == 540000) { + if (!source_tps3) + DRM_DEBUG_KMS("5.4 Gbps link rate without source HBR2/TPS3 support\n"); + if (!sink_tps3) + DRM_DEBUG_KMS("5.4 Gbps link rate without sink TPS3 support\n"); + } + + return training_pattern; +} + +static void +intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp) +{ + bool channel_eq = false; + int tries, cr_tries; + u32 training_pattern; + + training_pattern = intel_dp_training_pattern(intel_dp); /* channel equalization */ if (!intel_dp_set_link_train(intel_dp, diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c index 2a2ab306ad84..a2bd698fe2f7 100644 --- a/drivers/gpu/drm/i915/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/intel_dp_mst.c @@ -371,6 +371,8 @@ static enum drm_mode_status intel_dp_mst_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { + int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; + /* TODO - validate mode against available PBN for link */ if (mode->clock < 10000) return MODE_CLOCK_LOW; @@ -378,6 +380,9 @@ intel_dp_mst_mode_valid(struct drm_connector *connector, if (mode->flags & DRM_MODE_FLAG_DBLCLK) return MODE_H_ILLEGAL; + if (mode->clock > max_dotclk) + return MODE_CLOCK_HIGH; + return MODE_OK; } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index bf6f98134b50..4c027d69fac9 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -379,6 +379,7 @@ struct intel_crtc_state { bool update_pipe; /* can a fast modeset be performed? */ bool disable_cxsr; bool wm_changed; /* watermarks are updated */ + bool fb_changed; /* fb on any of the planes is changed */ /* Pipe source size (ie. panel fitter input size) * All planes will be positioned inside this space, @@ -492,6 +493,8 @@ struct intel_crtc_state { bool ips_enabled; + bool enable_fbc; + bool double_wide; bool dp_encoder_is_mst; @@ -542,16 +545,13 @@ struct intel_mmio_flip { */ struct intel_crtc_atomic_commit { /* Sleepable operations to perform before commit */ - bool disable_fbc; - bool disable_ips; - bool pre_disable_primary; /* Sleepable operations to perform after commit */ unsigned fb_bits; - bool wait_vblank; - bool update_fbc; bool post_enable_primary; - unsigned update_sprite_watermarks; + + /* Sleepable operations to perform before and after commit */ + bool update_fbc; }; struct intel_crtc { @@ -575,7 +575,7 @@ struct intel_crtc { /* Display surface base address adjustement for pageflips. Note that on * gen4+ this only adjusts up to a tile, offsets within a tile are * handled in the hw itself (with the TILEOFF register). */ - unsigned long dspaddr_offset; + u32 dspaddr_offset; int adjusted_x; int adjusted_y; @@ -770,9 +770,9 @@ struct intel_dp { int backlight_off_delay; struct delayed_work panel_vdd_work; bool want_panel_vdd; - unsigned long last_power_cycle; unsigned long last_power_on; unsigned long last_backlight_off; + ktime_t panel_power_off_time; struct notifier_block edp_notifier; @@ -909,9 +909,7 @@ struct intel_unpin_work { }; struct intel_load_detect_pipe { - struct drm_framebuffer *release_fb; - bool load_detect_temp; - int dpms_mode; + struct drm_atomic_state *restore_state; }; static inline struct intel_encoder * @@ -994,6 +992,8 @@ static inline bool intel_irqs_enabled(struct drm_i915_private *dev_priv) int intel_get_crtc_scanline(struct intel_crtc *crtc); void gen8_irq_power_well_post_enable(struct drm_i915_private *dev_priv, unsigned int pipe_mask); +void gen8_irq_power_well_pre_disable(struct drm_i915_private *dev_priv, + unsigned int pipe_mask); /* intel_crt.c */ void intel_crt_init(struct drm_device *dev); @@ -1172,11 +1172,11 @@ void assert_fdi_rx_pll(struct drm_i915_private *dev_priv, void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, bool state); #define assert_pipe_enabled(d, p) assert_pipe(d, p, true) #define assert_pipe_disabled(d, p) assert_pipe(d, p, false) -unsigned long intel_compute_tile_offset(struct drm_i915_private *dev_priv, - int *x, int *y, - uint64_t fb_modifier, - unsigned int cpp, - unsigned int pitch); +u32 intel_compute_tile_offset(struct drm_i915_private *dev_priv, + int *x, int *y, + uint64_t fb_modifier, + unsigned int cpp, + unsigned int pitch); void intel_prepare_reset(struct drm_device *dev); void intel_finish_reset(struct drm_device *dev); void hsw_enable_pc8(struct drm_i915_private *dev_priv); @@ -1226,7 +1226,7 @@ u32 skl_plane_ctl_rotation(unsigned int rotation); /* intel_csr.c */ void intel_csr_ucode_init(struct drm_i915_private *); -void intel_csr_load_program(struct drm_i915_private *); +bool intel_csr_load_program(struct drm_i915_private *); void intel_csr_ucode_fini(struct drm_i915_private *); /* intel_dp.c */ @@ -1327,13 +1327,16 @@ static inline void intel_fbdev_restore_mode(struct drm_device *dev) #endif /* intel_fbc.c */ +void intel_fbc_choose_crtc(struct drm_i915_private *dev_priv, + struct drm_atomic_state *state); bool intel_fbc_is_active(struct drm_i915_private *dev_priv); -void intel_fbc_deactivate(struct intel_crtc *crtc); -void intel_fbc_update(struct intel_crtc *crtc); +void intel_fbc_pre_update(struct intel_crtc *crtc); +void intel_fbc_post_update(struct intel_crtc *crtc); void intel_fbc_init(struct drm_i915_private *dev_priv); +void intel_fbc_init_pipe_state(struct drm_i915_private *dev_priv); void intel_fbc_enable(struct intel_crtc *crtc); -void intel_fbc_disable(struct drm_i915_private *dev_priv); -void intel_fbc_disable_crtc(struct intel_crtc *crtc); +void intel_fbc_disable(struct intel_crtc *crtc); +void intel_fbc_global_disable(struct drm_i915_private *dev_priv); void intel_fbc_invalidate(struct drm_i915_private *dev_priv, unsigned int frontbuffer_bits, enum fb_op_origin origin); @@ -1432,6 +1435,8 @@ bool __intel_display_power_is_enabled(struct drm_i915_private *dev_priv, enum intel_display_power_domain domain); void intel_display_power_get(struct drm_i915_private *dev_priv, enum intel_display_power_domain domain); +bool intel_display_power_get_if_enabled(struct drm_i915_private *dev_priv, + enum intel_display_power_domain domain); void intel_display_power_put(struct drm_i915_private *dev_priv, enum intel_display_power_domain domain); @@ -1518,6 +1523,7 @@ enable_rpm_wakeref_asserts(struct drm_i915_private *dev_priv) enable_rpm_wakeref_asserts(dev_priv) void intel_runtime_pm_get(struct drm_i915_private *dev_priv); +bool intel_runtime_pm_get_if_in_use(struct drm_i915_private *dev_priv); void intel_runtime_pm_get_noresume(struct drm_i915_private *dev_priv); void intel_runtime_pm_put(struct drm_i915_private *dev_priv); @@ -1559,6 +1565,7 @@ void skl_wm_get_hw_state(struct drm_device *dev); void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv, struct skl_ddb_allocation *ddb /* out */); uint32_t ilk_pipe_pixel_rate(const struct intel_crtc_state *pipe_config); +int sanitize_rc6_option(const struct drm_device *dev, int enable_rc6); /* intel_sdvo.c */ bool intel_sdvo_init(struct drm_device *dev, diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c index 91cef3525c93..01b8e9f4c272 100644 --- a/drivers/gpu/drm/i915/intel_dsi.c +++ b/drivers/gpu/drm/i915/intel_dsi.c @@ -478,8 +478,8 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder) DRM_DEBUG_KMS("\n"); - intel_dsi_prepare(encoder); intel_enable_dsi_pll(encoder); + intel_dsi_prepare(encoder); /* Panel Enable over CRC PMIC */ if (intel_dsi->gpio_panel) @@ -634,7 +634,6 @@ static void intel_dsi_post_disable(struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); - u32 val; DRM_DEBUG_KMS("\n"); @@ -642,9 +641,13 @@ static void intel_dsi_post_disable(struct intel_encoder *encoder) intel_dsi_clear_device_ready(encoder); - val = I915_READ(DSPCLK_GATE_D); - val &= ~DPOUNIT_CLOCK_GATE_DISABLE; - I915_WRITE(DSPCLK_GATE_D, val); + if (!IS_BROXTON(dev_priv)) { + u32 val; + + val = I915_READ(DSPCLK_GATE_D); + val &= ~DPOUNIT_CLOCK_GATE_DISABLE; + I915_WRITE(DSPCLK_GATE_D, val); + } drm_panel_unprepare(intel_dsi->panel); @@ -664,13 +667,16 @@ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder, struct drm_device *dev = encoder->base.dev; enum intel_display_power_domain power_domain; enum port port; + bool ret; DRM_DEBUG_KMS("\n"); power_domain = intel_display_port_power_domain(encoder); - if (!intel_display_power_is_enabled(dev_priv, power_domain)) + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) return false; + ret = false; + /* XXX: this only works for one DSI output */ for_each_dsi_port(port, intel_dsi->ports) { i915_reg_t ctrl_reg = IS_BROXTON(dev) ? @@ -691,12 +697,16 @@ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder, if (dpi_enabled || (func & CMD_MODE_DATA_WIDTH_MASK)) { if (I915_READ(MIPI_DEVICE_READY(port)) & DEVICE_READY) { *pipe = port == PORT_A ? PIPE_A : PIPE_B; - return true; + ret = true; + + goto out; } } } +out: + intel_display_power_put(dev_priv, power_domain); - return false; + return ret; } static void intel_dsi_get_config(struct intel_encoder *encoder, @@ -775,10 +785,9 @@ static void set_dsi_timings(struct drm_encoder *encoder, { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder); enum port port; - unsigned int bpp = intel_crtc->config->pipe_bpp; + unsigned int bpp = dsi_pixel_format_bpp(intel_dsi->pixel_format); unsigned int lane_count = intel_dsi->lane_count; u16 hactive, hfp, hsync, hbp, vfp, vsync, vbp; @@ -849,7 +858,7 @@ static void intel_dsi_prepare(struct intel_encoder *intel_encoder) struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder); const struct drm_display_mode *adjusted_mode = &intel_crtc->config->base.adjusted_mode; enum port port; - unsigned int bpp = intel_crtc->config->pipe_bpp; + unsigned int bpp = dsi_pixel_format_bpp(intel_dsi->pixel_format); u32 val, tmp; u16 mode_hdisplay; diff --git a/drivers/gpu/drm/i915/intel_dsi.h b/drivers/gpu/drm/i915/intel_dsi.h index de7be7f3fb42..92f39227b361 100644 --- a/drivers/gpu/drm/i915/intel_dsi.h +++ b/drivers/gpu/drm/i915/intel_dsi.h @@ -34,6 +34,8 @@ #define DSI_DUAL_LINK_FRONT_BACK 1 #define DSI_DUAL_LINK_PIXEL_ALT 2 +int dsi_pixel_format_bpp(int pixel_format); + struct intel_dsi_host; struct intel_dsi { diff --git a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c index 1d43e6f37fc1..7f145b4fec6a 100644 --- a/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c +++ b/drivers/gpu/drm/i915/intel_dsi_panel_vbt.c @@ -204,10 +204,28 @@ static const u8 *mipi_exec_gpio(struct intel_dsi *intel_dsi, const u8 *data) struct drm_device *dev = intel_dsi->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + if (dev_priv->vbt.dsi.seq_version >= 3) + data++; + gpio = *data++; /* pull up/down */ - action = *data++; + action = *data++ & 1; + + if (gpio >= ARRAY_SIZE(gtable)) { + DRM_DEBUG_KMS("unknown gpio %u\n", gpio); + goto out; + } + + if (!IS_VALLEYVIEW(dev_priv)) { + DRM_DEBUG_KMS("GPIO element not supported on this platform\n"); + goto out; + } + + if (dev_priv->vbt.dsi.seq_version >= 3) { + DRM_DEBUG_KMS("GPIO element v3 not supported\n"); + goto out; + } function = gtable[gpio].function_reg; pad = gtable[gpio].pad_reg; @@ -216,16 +234,18 @@ static const u8 *mipi_exec_gpio(struct intel_dsi *intel_dsi, const u8 *data) if (!gtable[gpio].init) { /* program the function */ /* FIXME: remove constant below */ - vlv_gpio_nc_write(dev_priv, function, 0x2000CC00); + vlv_iosf_sb_write(dev_priv, IOSF_PORT_GPIO_NC, function, + 0x2000CC00); gtable[gpio].init = 1; } val = 0x4 | action; /* pull up/down */ - vlv_gpio_nc_write(dev_priv, pad, val); + vlv_iosf_sb_write(dev_priv, IOSF_PORT_GPIO_NC, pad, val); mutex_unlock(&dev_priv->sb_lock); +out: return data; } @@ -420,10 +440,7 @@ struct drm_panel *vbt_panel_init(struct intel_dsi *intel_dsi, u16 panel_id) intel_dsi->dual_link = mipi_config->dual_link; intel_dsi->pixel_overlap = mipi_config->pixel_overlap; - if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB666) - bits_per_pixel = 18; - else if (intel_dsi->pixel_format == VID_MODE_FORMAT_RGB565) - bits_per_pixel = 16; + bits_per_pixel = dsi_pixel_format_bpp(intel_dsi->pixel_format); intel_dsi->operation_mode = mipi_config->is_cmd_mode; intel_dsi->video_mode_format = mipi_config->video_transfer_mode; diff --git a/drivers/gpu/drm/i915/intel_dsi_pll.c b/drivers/gpu/drm/i915/intel_dsi_pll.c index bb5e95a1a453..70883c54cb0a 100644 --- a/drivers/gpu/drm/i915/intel_dsi_pll.c +++ b/drivers/gpu/drm/i915/intel_dsi_pll.c @@ -30,7 +30,7 @@ #include "i915_drv.h" #include "intel_dsi.h" -static int dsi_pixel_format_bpp(int pixel_format) +int dsi_pixel_format_bpp(int pixel_format) { int bpp; diff --git a/drivers/gpu/drm/i915/intel_fbc.c b/drivers/gpu/drm/i915/intel_fbc.c index a1988a486b92..0f0492f4a357 100644 --- a/drivers/gpu/drm/i915/intel_fbc.c +++ b/drivers/gpu/drm/i915/intel_fbc.c @@ -43,7 +43,7 @@ static inline bool fbc_supported(struct drm_i915_private *dev_priv) { - return dev_priv->fbc.activate != NULL; + return HAS_FBC(dev_priv); } static inline bool fbc_on_pipe_a_only(struct drm_i915_private *dev_priv) @@ -56,6 +56,11 @@ static inline bool fbc_on_plane_a_only(struct drm_i915_private *dev_priv) return INTEL_INFO(dev_priv)->gen < 4; } +static inline bool no_fbc_on_multiple_pipes(struct drm_i915_private *dev_priv) +{ + return INTEL_INFO(dev_priv)->gen <= 3; +} + /* * In some platforms where the CRTC's x:0/y:0 coordinates doesn't match the * frontbuffer's x:0/y:0 coordinates we lie to the hardware about the plane's @@ -74,19 +79,17 @@ static unsigned int get_crtc_fence_y_offset(struct intel_crtc *crtc) * write to the PLANE_SIZE register. For BDW-, the hardware looks at the value * we wrote to PIPESRC. */ -static void intel_fbc_get_plane_source_size(struct intel_crtc *crtc, +static void intel_fbc_get_plane_source_size(struct intel_fbc_state_cache *cache, int *width, int *height) { - struct intel_plane_state *plane_state = - to_intel_plane_state(crtc->base.primary->state); int w, h; - if (intel_rotation_90_or_270(plane_state->base.rotation)) { - w = drm_rect_height(&plane_state->src) >> 16; - h = drm_rect_width(&plane_state->src) >> 16; + if (intel_rotation_90_or_270(cache->plane.rotation)) { + w = cache->plane.src_h; + h = cache->plane.src_w; } else { - w = drm_rect_width(&plane_state->src) >> 16; - h = drm_rect_height(&plane_state->src) >> 16; + w = cache->plane.src_w; + h = cache->plane.src_h; } if (width) @@ -95,26 +98,23 @@ static void intel_fbc_get_plane_source_size(struct intel_crtc *crtc, *height = h; } -static int intel_fbc_calculate_cfb_size(struct intel_crtc *crtc, - struct drm_framebuffer *fb) +static int intel_fbc_calculate_cfb_size(struct drm_i915_private *dev_priv, + struct intel_fbc_state_cache *cache) { - struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; int lines; - intel_fbc_get_plane_source_size(crtc, NULL, &lines); + intel_fbc_get_plane_source_size(cache, NULL, &lines); if (INTEL_INFO(dev_priv)->gen >= 7) lines = min(lines, 2048); /* Hardware needs the full buffer stride, not just the active area. */ - return lines * fb->pitches[0]; + return lines * cache->fb.stride; } static void i8xx_fbc_deactivate(struct drm_i915_private *dev_priv) { u32 fbc_ctl; - dev_priv->fbc.active = false; - /* Disable compression */ fbc_ctl = I915_READ(FBC_CONTROL); if ((fbc_ctl & FBC_CTL_EN) == 0) @@ -130,21 +130,17 @@ static void i8xx_fbc_deactivate(struct drm_i915_private *dev_priv) } } -static void i8xx_fbc_activate(struct intel_crtc *crtc) +static void i8xx_fbc_activate(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; - struct drm_framebuffer *fb = crtc->base.primary->fb; - struct drm_i915_gem_object *obj = intel_fb_obj(fb); + struct intel_fbc_reg_params *params = &dev_priv->fbc.params; int cfb_pitch; int i; u32 fbc_ctl; - dev_priv->fbc.active = true; - /* Note: fbc.threshold == 1 for i8xx */ - cfb_pitch = intel_fbc_calculate_cfb_size(crtc, fb) / FBC_LL_SIZE; - if (fb->pitches[0] < cfb_pitch) - cfb_pitch = fb->pitches[0]; + cfb_pitch = params->cfb_size / FBC_LL_SIZE; + if (params->fb.stride < cfb_pitch) + cfb_pitch = params->fb.stride; /* FBC_CTL wants 32B or 64B units */ if (IS_GEN2(dev_priv)) @@ -161,9 +157,9 @@ static void i8xx_fbc_activate(struct intel_crtc *crtc) /* Set it up... */ fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | FBC_CTL_CPU_FENCE; - fbc_ctl2 |= FBC_CTL_PLANE(crtc->plane); + fbc_ctl2 |= FBC_CTL_PLANE(params->crtc.plane); I915_WRITE(FBC_CONTROL2, fbc_ctl2); - I915_WRITE(FBC_FENCE_OFF, get_crtc_fence_y_offset(crtc)); + I915_WRITE(FBC_FENCE_OFF, params->crtc.fence_y_offset); } /* enable it... */ @@ -173,7 +169,7 @@ static void i8xx_fbc_activate(struct intel_crtc *crtc) if (IS_I945GM(dev_priv)) fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */ fbc_ctl |= (cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT; - fbc_ctl |= obj->fence_reg; + fbc_ctl |= params->fb.fence_reg; I915_WRITE(FBC_CONTROL, fbc_ctl); } @@ -182,23 +178,19 @@ static bool i8xx_fbc_is_active(struct drm_i915_private *dev_priv) return I915_READ(FBC_CONTROL) & FBC_CTL_EN; } -static void g4x_fbc_activate(struct intel_crtc *crtc) +static void g4x_fbc_activate(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; - struct drm_framebuffer *fb = crtc->base.primary->fb; - struct drm_i915_gem_object *obj = intel_fb_obj(fb); + struct intel_fbc_reg_params *params = &dev_priv->fbc.params; u32 dpfc_ctl; - dev_priv->fbc.active = true; - - dpfc_ctl = DPFC_CTL_PLANE(crtc->plane) | DPFC_SR_EN; - if (drm_format_plane_cpp(fb->pixel_format, 0) == 2) + dpfc_ctl = DPFC_CTL_PLANE(params->crtc.plane) | DPFC_SR_EN; + if (drm_format_plane_cpp(params->fb.pixel_format, 0) == 2) dpfc_ctl |= DPFC_CTL_LIMIT_2X; else dpfc_ctl |= DPFC_CTL_LIMIT_1X; - dpfc_ctl |= DPFC_CTL_FENCE_EN | obj->fence_reg; + dpfc_ctl |= DPFC_CTL_FENCE_EN | params->fb.fence_reg; - I915_WRITE(DPFC_FENCE_YOFF, get_crtc_fence_y_offset(crtc)); + I915_WRITE(DPFC_FENCE_YOFF, params->crtc.fence_y_offset); /* enable it... */ I915_WRITE(DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN); @@ -208,8 +200,6 @@ static void g4x_fbc_deactivate(struct drm_i915_private *dev_priv) { u32 dpfc_ctl; - dev_priv->fbc.active = false; - /* Disable compression */ dpfc_ctl = I915_READ(DPFC_CONTROL); if (dpfc_ctl & DPFC_CTL_EN) { @@ -230,19 +220,14 @@ static void intel_fbc_recompress(struct drm_i915_private *dev_priv) POSTING_READ(MSG_FBC_REND_STATE); } -static void ilk_fbc_activate(struct intel_crtc *crtc) +static void ilk_fbc_activate(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; - struct drm_framebuffer *fb = crtc->base.primary->fb; - struct drm_i915_gem_object *obj = intel_fb_obj(fb); + struct intel_fbc_reg_params *params = &dev_priv->fbc.params; u32 dpfc_ctl; int threshold = dev_priv->fbc.threshold; - unsigned int y_offset; - dev_priv->fbc.active = true; - - dpfc_ctl = DPFC_CTL_PLANE(crtc->plane); - if (drm_format_plane_cpp(fb->pixel_format, 0) == 2) + dpfc_ctl = DPFC_CTL_PLANE(params->crtc.plane); + if (drm_format_plane_cpp(params->fb.pixel_format, 0) == 2) threshold++; switch (threshold) { @@ -259,18 +244,17 @@ static void ilk_fbc_activate(struct intel_crtc *crtc) } dpfc_ctl |= DPFC_CTL_FENCE_EN; if (IS_GEN5(dev_priv)) - dpfc_ctl |= obj->fence_reg; + dpfc_ctl |= params->fb.fence_reg; - y_offset = get_crtc_fence_y_offset(crtc); - I915_WRITE(ILK_DPFC_FENCE_YOFF, y_offset); - I915_WRITE(ILK_FBC_RT_BASE, i915_gem_obj_ggtt_offset(obj) | ILK_FBC_RT_VALID); + I915_WRITE(ILK_DPFC_FENCE_YOFF, params->crtc.fence_y_offset); + I915_WRITE(ILK_FBC_RT_BASE, params->fb.ggtt_offset | ILK_FBC_RT_VALID); /* enable it... */ I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN); if (IS_GEN6(dev_priv)) { I915_WRITE(SNB_DPFC_CTL_SA, - SNB_CPU_FENCE_ENABLE | obj->fence_reg); - I915_WRITE(DPFC_CPU_FENCE_OFFSET, y_offset); + SNB_CPU_FENCE_ENABLE | params->fb.fence_reg); + I915_WRITE(DPFC_CPU_FENCE_OFFSET, params->crtc.fence_y_offset); } intel_fbc_recompress(dev_priv); @@ -280,8 +264,6 @@ static void ilk_fbc_deactivate(struct drm_i915_private *dev_priv) { u32 dpfc_ctl; - dev_priv->fbc.active = false; - /* Disable compression */ dpfc_ctl = I915_READ(ILK_DPFC_CONTROL); if (dpfc_ctl & DPFC_CTL_EN) { @@ -295,21 +277,17 @@ static bool ilk_fbc_is_active(struct drm_i915_private *dev_priv) return I915_READ(ILK_DPFC_CONTROL) & DPFC_CTL_EN; } -static void gen7_fbc_activate(struct intel_crtc *crtc) +static void gen7_fbc_activate(struct drm_i915_private *dev_priv) { - struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; - struct drm_framebuffer *fb = crtc->base.primary->fb; - struct drm_i915_gem_object *obj = intel_fb_obj(fb); + struct intel_fbc_reg_params *params = &dev_priv->fbc.params; u32 dpfc_ctl; int threshold = dev_priv->fbc.threshold; - dev_priv->fbc.active = true; - dpfc_ctl = 0; if (IS_IVYBRIDGE(dev_priv)) - dpfc_ctl |= IVB_DPFC_CTL_PLANE(crtc->plane); + dpfc_ctl |= IVB_DPFC_CTL_PLANE(params->crtc.plane); - if (drm_format_plane_cpp(fb->pixel_format, 0) == 2) + if (drm_format_plane_cpp(params->fb.pixel_format, 0) == 2) threshold++; switch (threshold) { @@ -337,20 +315,60 @@ static void gen7_fbc_activate(struct intel_crtc *crtc) ILK_FBCQ_DIS); } else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) { /* WaFbcAsynchFlipDisableFbcQueue:hsw,bdw */ - I915_WRITE(CHICKEN_PIPESL_1(crtc->pipe), - I915_READ(CHICKEN_PIPESL_1(crtc->pipe)) | + I915_WRITE(CHICKEN_PIPESL_1(params->crtc.pipe), + I915_READ(CHICKEN_PIPESL_1(params->crtc.pipe)) | HSW_FBCQ_DIS); } I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN); I915_WRITE(SNB_DPFC_CTL_SA, - SNB_CPU_FENCE_ENABLE | obj->fence_reg); - I915_WRITE(DPFC_CPU_FENCE_OFFSET, get_crtc_fence_y_offset(crtc)); + SNB_CPU_FENCE_ENABLE | params->fb.fence_reg); + I915_WRITE(DPFC_CPU_FENCE_OFFSET, params->crtc.fence_y_offset); intel_fbc_recompress(dev_priv); } +static bool intel_fbc_hw_is_active(struct drm_i915_private *dev_priv) +{ + if (INTEL_INFO(dev_priv)->gen >= 5) + return ilk_fbc_is_active(dev_priv); + else if (IS_GM45(dev_priv)) + return g4x_fbc_is_active(dev_priv); + else + return i8xx_fbc_is_active(dev_priv); +} + +static void intel_fbc_hw_activate(struct drm_i915_private *dev_priv) +{ + struct intel_fbc *fbc = &dev_priv->fbc; + + fbc->active = true; + + if (INTEL_INFO(dev_priv)->gen >= 7) + gen7_fbc_activate(dev_priv); + else if (INTEL_INFO(dev_priv)->gen >= 5) + ilk_fbc_activate(dev_priv); + else if (IS_GM45(dev_priv)) + g4x_fbc_activate(dev_priv); + else + i8xx_fbc_activate(dev_priv); +} + +static void intel_fbc_hw_deactivate(struct drm_i915_private *dev_priv) +{ + struct intel_fbc *fbc = &dev_priv->fbc; + + fbc->active = false; + + if (INTEL_INFO(dev_priv)->gen >= 5) + ilk_fbc_deactivate(dev_priv); + else if (IS_GM45(dev_priv)) + g4x_fbc_deactivate(dev_priv); + else + i8xx_fbc_deactivate(dev_priv); +} + /** * intel_fbc_is_active - Is FBC active? * @dev_priv: i915 device instance @@ -364,24 +382,24 @@ bool intel_fbc_is_active(struct drm_i915_private *dev_priv) return dev_priv->fbc.active; } -static void intel_fbc_activate(const struct drm_framebuffer *fb) -{ - struct drm_i915_private *dev_priv = fb->dev->dev_private; - struct intel_crtc *crtc = dev_priv->fbc.crtc; - - dev_priv->fbc.activate(crtc); - - dev_priv->fbc.fb_id = fb->base.id; - dev_priv->fbc.y = crtc->base.y; -} - static void intel_fbc_work_fn(struct work_struct *__work) { struct drm_i915_private *dev_priv = container_of(__work, struct drm_i915_private, fbc.work.work); - struct intel_fbc_work *work = &dev_priv->fbc.work; - struct intel_crtc *crtc = dev_priv->fbc.crtc; - int delay_ms = 50; + struct intel_fbc *fbc = &dev_priv->fbc; + struct intel_fbc_work *work = &fbc->work; + struct intel_crtc *crtc = fbc->crtc; + struct drm_vblank_crtc *vblank = &dev_priv->dev->vblank[crtc->pipe]; + + if (drm_crtc_vblank_get(&crtc->base)) { + DRM_ERROR("vblank not available for FBC on pipe %c\n", + pipe_name(crtc->pipe)); + + mutex_lock(&fbc->lock); + work->scheduled = false; + mutex_unlock(&fbc->lock); + return; + } retry: /* Delay the actual enabling to let pageflipping cease and the @@ -390,142 +408,97 @@ retry: * vblank to pass after disabling the FBC before we attempt * to modify the control registers. * - * A more complicated solution would involve tracking vblanks - * following the termination of the page-flipping sequence - * and indeed performing the enable as a co-routine and not - * waiting synchronously upon the vblank. - * * WaFbcWaitForVBlankBeforeEnable:ilk,snb + * + * It is also worth mentioning that since work->scheduled_vblank can be + * updated multiple times by the other threads, hitting the timeout is + * not an error condition. We'll just end up hitting the "goto retry" + * case below. */ - wait_remaining_ms_from_jiffies(work->enable_jiffies, delay_ms); + wait_event_timeout(vblank->queue, + drm_crtc_vblank_count(&crtc->base) != work->scheduled_vblank, + msecs_to_jiffies(50)); - mutex_lock(&dev_priv->fbc.lock); + mutex_lock(&fbc->lock); /* Were we cancelled? */ if (!work->scheduled) goto out; /* Were we delayed again while this function was sleeping? */ - if (time_after(work->enable_jiffies + msecs_to_jiffies(delay_ms), - jiffies)) { - mutex_unlock(&dev_priv->fbc.lock); + if (drm_crtc_vblank_count(&crtc->base) == work->scheduled_vblank) { + mutex_unlock(&fbc->lock); goto retry; } - if (crtc->base.primary->fb == work->fb) - intel_fbc_activate(work->fb); + intel_fbc_hw_activate(dev_priv); work->scheduled = false; out: - mutex_unlock(&dev_priv->fbc.lock); -} - -static void intel_fbc_cancel_work(struct drm_i915_private *dev_priv) -{ - WARN_ON(!mutex_is_locked(&dev_priv->fbc.lock)); - dev_priv->fbc.work.scheduled = false; + mutex_unlock(&fbc->lock); + drm_crtc_vblank_put(&crtc->base); } static void intel_fbc_schedule_activation(struct intel_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; - struct intel_fbc_work *work = &dev_priv->fbc.work; + struct intel_fbc *fbc = &dev_priv->fbc; + struct intel_fbc_work *work = &fbc->work; + + WARN_ON(!mutex_is_locked(&fbc->lock)); - WARN_ON(!mutex_is_locked(&dev_priv->fbc.lock)); + if (drm_crtc_vblank_get(&crtc->base)) { + DRM_ERROR("vblank not available for FBC on pipe %c\n", + pipe_name(crtc->pipe)); + return; + } - /* It is useless to call intel_fbc_cancel_work() in this function since - * we're not releasing fbc.lock, so it won't have an opportunity to grab - * it to discover that it was cancelled. So we just update the expected - * jiffy count. */ - work->fb = crtc->base.primary->fb; + /* It is useless to call intel_fbc_cancel_work() or cancel_work() in + * this function since we're not releasing fbc.lock, so it won't have an + * opportunity to grab it to discover that it was cancelled. So we just + * update the expected jiffy count. */ work->scheduled = true; - work->enable_jiffies = jiffies; + work->scheduled_vblank = drm_crtc_vblank_count(&crtc->base); + drm_crtc_vblank_put(&crtc->base); schedule_work(&work->work); } -static void __intel_fbc_deactivate(struct drm_i915_private *dev_priv) +static void intel_fbc_deactivate(struct drm_i915_private *dev_priv) { - WARN_ON(!mutex_is_locked(&dev_priv->fbc.lock)); - - intel_fbc_cancel_work(dev_priv); - - if (dev_priv->fbc.active) - dev_priv->fbc.deactivate(dev_priv); -} - -/* - * intel_fbc_deactivate - deactivate FBC if it's associated with crtc - * @crtc: the CRTC - * - * This function deactivates FBC if it's associated with the provided CRTC. - */ -void intel_fbc_deactivate(struct intel_crtc *crtc) -{ - struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; - - if (!fbc_supported(dev_priv)) - return; + struct intel_fbc *fbc = &dev_priv->fbc; - mutex_lock(&dev_priv->fbc.lock); - if (dev_priv->fbc.crtc == crtc) - __intel_fbc_deactivate(dev_priv); - mutex_unlock(&dev_priv->fbc.lock); -} + WARN_ON(!mutex_is_locked(&fbc->lock)); -static void set_no_fbc_reason(struct drm_i915_private *dev_priv, - const char *reason) -{ - if (dev_priv->fbc.no_fbc_reason == reason) - return; + /* Calling cancel_work() here won't help due to the fact that the work + * function grabs fbc->lock. Just set scheduled to false so the work + * function can know it was cancelled. */ + fbc->work.scheduled = false; - dev_priv->fbc.no_fbc_reason = reason; - DRM_DEBUG_KMS("Disabling FBC: %s\n", reason); + if (fbc->active) + intel_fbc_hw_deactivate(dev_priv); } -static bool crtc_can_fbc(struct intel_crtc *crtc) +static bool multiple_pipes_ok(struct intel_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct drm_plane *primary = crtc->base.primary; + struct intel_fbc *fbc = &dev_priv->fbc; + enum pipe pipe = crtc->pipe; - if (fbc_on_pipe_a_only(dev_priv) && crtc->pipe != PIPE_A) - return false; - - if (fbc_on_plane_a_only(dev_priv) && crtc->plane != PLANE_A) - return false; - - return true; -} - -static bool crtc_is_valid(struct intel_crtc *crtc) -{ - if (!intel_crtc_active(&crtc->base)) - return false; - - if (!to_intel_plane_state(crtc->base.primary->state)->visible) - return false; - - return true; -} - -static bool multiple_pipes_ok(struct drm_i915_private *dev_priv) -{ - enum pipe pipe; - int n_pipes = 0; - struct drm_crtc *crtc; - - if (INTEL_INFO(dev_priv)->gen > 4) + /* Don't even bother tracking anything we don't need. */ + if (!no_fbc_on_multiple_pipes(dev_priv)) return true; - for_each_pipe(dev_priv, pipe) { - crtc = dev_priv->pipe_to_crtc_mapping[pipe]; + WARN_ON(!drm_modeset_is_locked(&primary->mutex)); - if (intel_crtc_active(crtc) && - to_intel_plane_state(crtc->primary->state)->visible) - n_pipes++; - } + if (to_intel_plane_state(primary->state)->visible) + fbc->visible_pipes_mask |= (1 << pipe); + else + fbc->visible_pipes_mask &= ~(1 << pipe); - return (n_pipes < 2); + return (fbc->visible_pipes_mask & ~(1 << pipe)) != 0; } static int find_compression_threshold(struct drm_i915_private *dev_priv, @@ -581,16 +554,16 @@ again: static int intel_fbc_alloc_cfb(struct intel_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; - struct drm_framebuffer *fb = crtc->base.primary->state->fb; + struct intel_fbc *fbc = &dev_priv->fbc; struct drm_mm_node *uninitialized_var(compressed_llb); int size, fb_cpp, ret; - WARN_ON(drm_mm_node_allocated(&dev_priv->fbc.compressed_fb)); + WARN_ON(drm_mm_node_allocated(&fbc->compressed_fb)); - size = intel_fbc_calculate_cfb_size(crtc, fb); - fb_cpp = drm_format_plane_cpp(fb->pixel_format, 0); + size = intel_fbc_calculate_cfb_size(dev_priv, &fbc->state_cache); + fb_cpp = drm_format_plane_cpp(fbc->state_cache.fb.pixel_format, 0); - ret = find_compression_threshold(dev_priv, &dev_priv->fbc.compressed_fb, + ret = find_compression_threshold(dev_priv, &fbc->compressed_fb, size, fb_cpp); if (!ret) goto err_llb; @@ -599,12 +572,12 @@ static int intel_fbc_alloc_cfb(struct intel_crtc *crtc) } - dev_priv->fbc.threshold = ret; + fbc->threshold = ret; if (INTEL_INFO(dev_priv)->gen >= 5) - I915_WRITE(ILK_DPFC_CB_BASE, dev_priv->fbc.compressed_fb.start); + I915_WRITE(ILK_DPFC_CB_BASE, fbc->compressed_fb.start); else if (IS_GM45(dev_priv)) { - I915_WRITE(DPFC_CB_BASE, dev_priv->fbc.compressed_fb.start); + I915_WRITE(DPFC_CB_BASE, fbc->compressed_fb.start); } else { compressed_llb = kzalloc(sizeof(*compressed_llb), GFP_KERNEL); if (!compressed_llb) @@ -615,23 +588,22 @@ static int intel_fbc_alloc_cfb(struct intel_crtc *crtc) if (ret) goto err_fb; - dev_priv->fbc.compressed_llb = compressed_llb; + fbc->compressed_llb = compressed_llb; I915_WRITE(FBC_CFB_BASE, - dev_priv->mm.stolen_base + dev_priv->fbc.compressed_fb.start); + dev_priv->mm.stolen_base + fbc->compressed_fb.start); I915_WRITE(FBC_LL_BASE, dev_priv->mm.stolen_base + compressed_llb->start); } DRM_DEBUG_KMS("reserved %llu bytes of contiguous stolen space for FBC, threshold: %d\n", - dev_priv->fbc.compressed_fb.size, - dev_priv->fbc.threshold); + fbc->compressed_fb.size, fbc->threshold); return 0; err_fb: kfree(compressed_llb); - i915_gem_stolen_remove_node(dev_priv, &dev_priv->fbc.compressed_fb); + i915_gem_stolen_remove_node(dev_priv, &fbc->compressed_fb); err_llb: pr_info_once("drm: not enough stolen space for compressed buffer (need %d more bytes), disabling. Hint: you may be able to increase stolen memory size in the BIOS to avoid this.\n", size); return -ENOSPC; @@ -639,25 +611,27 @@ err_llb: static void __intel_fbc_cleanup_cfb(struct drm_i915_private *dev_priv) { - if (drm_mm_node_allocated(&dev_priv->fbc.compressed_fb)) - i915_gem_stolen_remove_node(dev_priv, - &dev_priv->fbc.compressed_fb); - - if (dev_priv->fbc.compressed_llb) { - i915_gem_stolen_remove_node(dev_priv, - dev_priv->fbc.compressed_llb); - kfree(dev_priv->fbc.compressed_llb); + struct intel_fbc *fbc = &dev_priv->fbc; + + if (drm_mm_node_allocated(&fbc->compressed_fb)) + i915_gem_stolen_remove_node(dev_priv, &fbc->compressed_fb); + + if (fbc->compressed_llb) { + i915_gem_stolen_remove_node(dev_priv, fbc->compressed_llb); + kfree(fbc->compressed_llb); } } void intel_fbc_cleanup_cfb(struct drm_i915_private *dev_priv) { + struct intel_fbc *fbc = &dev_priv->fbc; + if (!fbc_supported(dev_priv)) return; - mutex_lock(&dev_priv->fbc.lock); + mutex_lock(&fbc->lock); __intel_fbc_cleanup_cfb(dev_priv); - mutex_unlock(&dev_priv->fbc.lock); + mutex_unlock(&fbc->lock); } static bool stride_is_valid(struct drm_i915_private *dev_priv, @@ -681,19 +655,17 @@ static bool stride_is_valid(struct drm_i915_private *dev_priv, return true; } -static bool pixel_format_is_valid(struct drm_framebuffer *fb) +static bool pixel_format_is_valid(struct drm_i915_private *dev_priv, + uint32_t pixel_format) { - struct drm_device *dev = fb->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - - switch (fb->pixel_format) { + switch (pixel_format) { case DRM_FORMAT_XRGB8888: case DRM_FORMAT_XBGR8888: return true; case DRM_FORMAT_XRGB1555: case DRM_FORMAT_RGB565: /* 16bpp not supported on gen2 */ - if (IS_GEN2(dev)) + if (IS_GEN2(dev_priv)) return false; /* WaFbcOnly1to1Ratio:ctg */ if (IS_G4X(dev_priv)) @@ -713,6 +685,7 @@ static bool pixel_format_is_valid(struct drm_framebuffer *fb) static bool intel_fbc_hw_tracking_covers_screen(struct intel_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct intel_fbc *fbc = &dev_priv->fbc; unsigned int effective_w, effective_h, max_w, max_h; if (INTEL_INFO(dev_priv)->gen >= 8 || IS_HASWELL(dev_priv)) { @@ -726,87 +699,105 @@ static bool intel_fbc_hw_tracking_covers_screen(struct intel_crtc *crtc) max_h = 1536; } - intel_fbc_get_plane_source_size(crtc, &effective_w, &effective_h); + intel_fbc_get_plane_source_size(&fbc->state_cache, &effective_w, + &effective_h); effective_w += crtc->adjusted_x; effective_h += crtc->adjusted_y; return effective_w <= max_w && effective_h <= max_h; } -/** - * __intel_fbc_update - activate/deactivate FBC as needed, unlocked - * @crtc: the CRTC that triggered the update - * - * This function completely reevaluates the status of FBC, then activates, - * deactivates or maintains it on the same state. - */ -static void __intel_fbc_update(struct intel_crtc *crtc) +static void intel_fbc_update_state_cache(struct intel_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; - struct drm_framebuffer *fb; + struct intel_fbc *fbc = &dev_priv->fbc; + struct intel_fbc_state_cache *cache = &fbc->state_cache; + struct intel_crtc_state *crtc_state = + to_intel_crtc_state(crtc->base.state); + struct intel_plane_state *plane_state = + to_intel_plane_state(crtc->base.primary->state); + struct drm_framebuffer *fb = plane_state->base.fb; struct drm_i915_gem_object *obj; - const struct drm_display_mode *adjusted_mode; - WARN_ON(!mutex_is_locked(&dev_priv->fbc.lock)); + WARN_ON(!drm_modeset_is_locked(&crtc->base.mutex)); + WARN_ON(!drm_modeset_is_locked(&crtc->base.primary->mutex)); - if (!multiple_pipes_ok(dev_priv)) { - set_no_fbc_reason(dev_priv, "more than one pipe active"); - goto out_disable; - } + cache->crtc.mode_flags = crtc_state->base.adjusted_mode.flags; + if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) + cache->crtc.hsw_bdw_pixel_rate = + ilk_pipe_pixel_rate(crtc_state); - if (!dev_priv->fbc.enabled || dev_priv->fbc.crtc != crtc) - return; + cache->plane.rotation = plane_state->base.rotation; + cache->plane.src_w = drm_rect_width(&plane_state->src) >> 16; + cache->plane.src_h = drm_rect_height(&plane_state->src) >> 16; + cache->plane.visible = plane_state->visible; - if (!crtc_is_valid(crtc)) { - set_no_fbc_reason(dev_priv, "no output"); - goto out_disable; - } + if (!cache->plane.visible) + return; - fb = crtc->base.primary->fb; obj = intel_fb_obj(fb); - adjusted_mode = &crtc->config->base.adjusted_mode; - if ((adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) || - (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)) { - set_no_fbc_reason(dev_priv, "incompatible mode"); - goto out_disable; + /* FIXME: We lack the proper locking here, so only run this on the + * platforms that need. */ + if (INTEL_INFO(dev_priv)->gen >= 5 && INTEL_INFO(dev_priv)->gen < 7) + cache->fb.ilk_ggtt_offset = i915_gem_obj_ggtt_offset(obj); + cache->fb.pixel_format = fb->pixel_format; + cache->fb.stride = fb->pitches[0]; + cache->fb.fence_reg = obj->fence_reg; + cache->fb.tiling_mode = obj->tiling_mode; +} + +static bool intel_fbc_can_activate(struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct intel_fbc *fbc = &dev_priv->fbc; + struct intel_fbc_state_cache *cache = &fbc->state_cache; + + if (!cache->plane.visible) { + fbc->no_fbc_reason = "primary plane not visible"; + return false; + } + + if ((cache->crtc.mode_flags & DRM_MODE_FLAG_INTERLACE) || + (cache->crtc.mode_flags & DRM_MODE_FLAG_DBLSCAN)) { + fbc->no_fbc_reason = "incompatible mode"; + return false; } if (!intel_fbc_hw_tracking_covers_screen(crtc)) { - set_no_fbc_reason(dev_priv, "mode too large for compression"); - goto out_disable; + fbc->no_fbc_reason = "mode too large for compression"; + return false; } /* The use of a CPU fence is mandatory in order to detect writes * by the CPU to the scanout and trigger updates to the FBC. */ - if (obj->tiling_mode != I915_TILING_X || - obj->fence_reg == I915_FENCE_REG_NONE) { - set_no_fbc_reason(dev_priv, "framebuffer not tiled or fenced"); - goto out_disable; + if (cache->fb.tiling_mode != I915_TILING_X || + cache->fb.fence_reg == I915_FENCE_REG_NONE) { + fbc->no_fbc_reason = "framebuffer not tiled or fenced"; + return false; } if (INTEL_INFO(dev_priv)->gen <= 4 && !IS_G4X(dev_priv) && - crtc->base.primary->state->rotation != BIT(DRM_ROTATE_0)) { - set_no_fbc_reason(dev_priv, "rotation unsupported"); - goto out_disable; + cache->plane.rotation != BIT(DRM_ROTATE_0)) { + fbc->no_fbc_reason = "rotation unsupported"; + return false; } - if (!stride_is_valid(dev_priv, fb->pitches[0])) { - set_no_fbc_reason(dev_priv, "framebuffer stride not supported"); - goto out_disable; + if (!stride_is_valid(dev_priv, cache->fb.stride)) { + fbc->no_fbc_reason = "framebuffer stride not supported"; + return false; } - if (!pixel_format_is_valid(fb)) { - set_no_fbc_reason(dev_priv, "pixel format is invalid"); - goto out_disable; + if (!pixel_format_is_valid(dev_priv, cache->fb.pixel_format)) { + fbc->no_fbc_reason = "pixel format is invalid"; + return false; } /* WaFbcExceedCdClockThreshold:hsw,bdw */ if ((IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) && - ilk_pipe_pixel_rate(crtc->config) >= - dev_priv->cdclk_freq * 95 / 100) { - set_no_fbc_reason(dev_priv, "pixel rate is too big"); - goto out_disable; + cache->crtc.hsw_bdw_pixel_rate >= dev_priv->cdclk_freq * 95 / 100) { + fbc->no_fbc_reason = "pixel rate is too big"; + return false; } /* It is possible for the required CFB size change without a @@ -819,189 +810,322 @@ static void __intel_fbc_update(struct intel_crtc *crtc) * we didn't get any invalidate/deactivate calls, but this would require * a lot of tracking just for a specific case. If we conclude it's an * important case, we can implement it later. */ - if (intel_fbc_calculate_cfb_size(crtc, fb) > - dev_priv->fbc.compressed_fb.size * dev_priv->fbc.threshold) { - set_no_fbc_reason(dev_priv, "CFB requirements changed"); - goto out_disable; + if (intel_fbc_calculate_cfb_size(dev_priv, &fbc->state_cache) > + fbc->compressed_fb.size * fbc->threshold) { + fbc->no_fbc_reason = "CFB requirements changed"; + return false; } + return true; +} + +static bool intel_fbc_can_choose(struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct intel_fbc *fbc = &dev_priv->fbc; + bool enable_by_default = IS_HASWELL(dev_priv) || + IS_BROADWELL(dev_priv); + + if (intel_vgpu_active(dev_priv->dev)) { + fbc->no_fbc_reason = "VGPU is active"; + return false; + } + + if (i915.enable_fbc < 0 && !enable_by_default) { + fbc->no_fbc_reason = "disabled per chip default"; + return false; + } + + if (!i915.enable_fbc) { + fbc->no_fbc_reason = "disabled per module param"; + return false; + } + + if (fbc_on_pipe_a_only(dev_priv) && crtc->pipe != PIPE_A) { + fbc->no_fbc_reason = "no enabled pipes can have FBC"; + return false; + } + + if (fbc_on_plane_a_only(dev_priv) && crtc->plane != PLANE_A) { + fbc->no_fbc_reason = "no enabled planes can have FBC"; + return false; + } + + return true; +} + +static void intel_fbc_get_reg_params(struct intel_crtc *crtc, + struct intel_fbc_reg_params *params) +{ + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct intel_fbc *fbc = &dev_priv->fbc; + struct intel_fbc_state_cache *cache = &fbc->state_cache; + + /* Since all our fields are integer types, use memset here so the + * comparison function can rely on memcmp because the padding will be + * zero. */ + memset(params, 0, sizeof(*params)); + + params->crtc.pipe = crtc->pipe; + params->crtc.plane = crtc->plane; + params->crtc.fence_y_offset = get_crtc_fence_y_offset(crtc); + + params->fb.pixel_format = cache->fb.pixel_format; + params->fb.stride = cache->fb.stride; + params->fb.fence_reg = cache->fb.fence_reg; + + params->cfb_size = intel_fbc_calculate_cfb_size(dev_priv, cache); + + params->fb.ggtt_offset = cache->fb.ilk_ggtt_offset; +} + +static bool intel_fbc_reg_params_equal(struct intel_fbc_reg_params *params1, + struct intel_fbc_reg_params *params2) +{ + /* We can use this since intel_fbc_get_reg_params() does a memset. */ + return memcmp(params1, params2, sizeof(*params1)) == 0; +} + +void intel_fbc_pre_update(struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct intel_fbc *fbc = &dev_priv->fbc; + + if (!fbc_supported(dev_priv)) + return; + + mutex_lock(&fbc->lock); + + if (!multiple_pipes_ok(crtc)) { + fbc->no_fbc_reason = "more than one pipe active"; + goto deactivate; + } + + if (!fbc->enabled || fbc->crtc != crtc) + goto unlock; + + intel_fbc_update_state_cache(crtc); + +deactivate: + intel_fbc_deactivate(dev_priv); +unlock: + mutex_unlock(&fbc->lock); +} + +static void __intel_fbc_post_update(struct intel_crtc *crtc) +{ + struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct intel_fbc *fbc = &dev_priv->fbc; + struct intel_fbc_reg_params old_params; + + WARN_ON(!mutex_is_locked(&fbc->lock)); + + if (!fbc->enabled || fbc->crtc != crtc) + return; + + if (!intel_fbc_can_activate(crtc)) { + WARN_ON(fbc->active); + return; + } + + old_params = fbc->params; + intel_fbc_get_reg_params(crtc, &fbc->params); + /* If the scanout has not changed, don't modify the FBC settings. * Note that we make the fundamental assumption that the fb->obj * cannot be unpinned (and have its GTT offset and fence revoked) * without first being decoupled from the scanout and FBC disabled. */ - if (dev_priv->fbc.crtc == crtc && - dev_priv->fbc.fb_id == fb->base.id && - dev_priv->fbc.y == crtc->base.y && - dev_priv->fbc.active) + if (fbc->active && + intel_fbc_reg_params_equal(&old_params, &fbc->params)) return; - if (intel_fbc_is_active(dev_priv)) { - /* We update FBC along two paths, after changing fb/crtc - * configuration (modeswitching) and after page-flipping - * finishes. For the latter, we know that not only did - * we disable the FBC at the start of the page-flip - * sequence, but also more than one vblank has passed. - * - * For the former case of modeswitching, it is possible - * to switch between two FBC valid configurations - * instantaneously so we do need to disable the FBC - * before we can modify its control registers. We also - * have to wait for the next vblank for that to take - * effect. However, since we delay enabling FBC we can - * assume that a vblank has passed since disabling and - * that we can safely alter the registers in the deferred - * callback. - * - * In the scenario that we go from a valid to invalid - * and then back to valid FBC configuration we have - * no strict enforcement that a vblank occurred since - * disabling the FBC. However, along all current pipe - * disabling paths we do need to wait for a vblank at - * some point. And we wait before enabling FBC anyway. - */ - DRM_DEBUG_KMS("deactivating FBC for update\n"); - __intel_fbc_deactivate(dev_priv); - } - + intel_fbc_deactivate(dev_priv); intel_fbc_schedule_activation(crtc); - dev_priv->fbc.no_fbc_reason = "FBC enabled (not necessarily active)"; - return; - -out_disable: - /* Multiple disables should be harmless */ - if (intel_fbc_is_active(dev_priv)) { - DRM_DEBUG_KMS("unsupported config, deactivating FBC\n"); - __intel_fbc_deactivate(dev_priv); - } + fbc->no_fbc_reason = "FBC enabled (active or scheduled)"; } -/* - * intel_fbc_update - activate/deactivate FBC as needed - * @crtc: the CRTC that triggered the update - * - * This function reevaluates the overall state and activates or deactivates FBC. - */ -void intel_fbc_update(struct intel_crtc *crtc) +void intel_fbc_post_update(struct intel_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct intel_fbc *fbc = &dev_priv->fbc; if (!fbc_supported(dev_priv)) return; - mutex_lock(&dev_priv->fbc.lock); - __intel_fbc_update(crtc); - mutex_unlock(&dev_priv->fbc.lock); + mutex_lock(&fbc->lock); + __intel_fbc_post_update(crtc); + mutex_unlock(&fbc->lock); +} + +static unsigned int intel_fbc_get_frontbuffer_bit(struct intel_fbc *fbc) +{ + if (fbc->enabled) + return to_intel_plane(fbc->crtc->base.primary)->frontbuffer_bit; + else + return fbc->possible_framebuffer_bits; } void intel_fbc_invalidate(struct drm_i915_private *dev_priv, unsigned int frontbuffer_bits, enum fb_op_origin origin) { - unsigned int fbc_bits; + struct intel_fbc *fbc = &dev_priv->fbc; if (!fbc_supported(dev_priv)) return; - if (origin == ORIGIN_GTT) + if (origin == ORIGIN_GTT || origin == ORIGIN_FLIP) return; - mutex_lock(&dev_priv->fbc.lock); + mutex_lock(&fbc->lock); - if (dev_priv->fbc.enabled) - fbc_bits = INTEL_FRONTBUFFER_PRIMARY(dev_priv->fbc.crtc->pipe); - else - fbc_bits = dev_priv->fbc.possible_framebuffer_bits; - - dev_priv->fbc.busy_bits |= (fbc_bits & frontbuffer_bits); + fbc->busy_bits |= intel_fbc_get_frontbuffer_bit(fbc) & frontbuffer_bits; - if (dev_priv->fbc.busy_bits) - __intel_fbc_deactivate(dev_priv); + if (fbc->enabled && fbc->busy_bits) + intel_fbc_deactivate(dev_priv); - mutex_unlock(&dev_priv->fbc.lock); + mutex_unlock(&fbc->lock); } void intel_fbc_flush(struct drm_i915_private *dev_priv, unsigned int frontbuffer_bits, enum fb_op_origin origin) { + struct intel_fbc *fbc = &dev_priv->fbc; + if (!fbc_supported(dev_priv)) return; - if (origin == ORIGIN_GTT) + if (origin == ORIGIN_GTT || origin == ORIGIN_FLIP) return; - mutex_lock(&dev_priv->fbc.lock); + mutex_lock(&fbc->lock); - dev_priv->fbc.busy_bits &= ~frontbuffer_bits; + fbc->busy_bits &= ~frontbuffer_bits; - if (!dev_priv->fbc.busy_bits && dev_priv->fbc.enabled) { - if (origin != ORIGIN_FLIP && dev_priv->fbc.active) { + if (!fbc->busy_bits && fbc->enabled && + (frontbuffer_bits & intel_fbc_get_frontbuffer_bit(fbc))) { + if (fbc->active) intel_fbc_recompress(dev_priv); - } else { - __intel_fbc_deactivate(dev_priv); - __intel_fbc_update(dev_priv->fbc.crtc); + else + __intel_fbc_post_update(fbc->crtc); + } + + mutex_unlock(&fbc->lock); +} + +/** + * intel_fbc_choose_crtc - select a CRTC to enable FBC on + * @dev_priv: i915 device instance + * @state: the atomic state structure + * + * This function looks at the proposed state for CRTCs and planes, then chooses + * which pipe is going to have FBC by setting intel_crtc_state->enable_fbc to + * true. + * + * Later, intel_fbc_enable is going to look for state->enable_fbc and then maybe + * enable FBC for the chosen CRTC. If it does, it will set dev_priv->fbc.crtc. + */ +void intel_fbc_choose_crtc(struct drm_i915_private *dev_priv, + struct drm_atomic_state *state) +{ + struct intel_fbc *fbc = &dev_priv->fbc; + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_plane *plane; + struct drm_plane_state *plane_state; + bool fbc_crtc_present = false; + int i, j; + + mutex_lock(&fbc->lock); + + for_each_crtc_in_state(state, crtc, crtc_state, i) { + if (fbc->crtc == to_intel_crtc(crtc)) { + fbc_crtc_present = true; + break; + } + } + /* This atomic commit doesn't involve the CRTC currently tied to FBC. */ + if (!fbc_crtc_present && fbc->crtc != NULL) + goto out; + + /* Simply choose the first CRTC that is compatible and has a visible + * plane. We could go for fancier schemes such as checking the plane + * size, but this would just affect the few platforms that don't tie FBC + * to pipe or plane A. */ + for_each_plane_in_state(state, plane, plane_state, i) { + struct intel_plane_state *intel_plane_state = + to_intel_plane_state(plane_state); + + if (!intel_plane_state->visible) + continue; + + for_each_crtc_in_state(state, crtc, crtc_state, j) { + struct intel_crtc_state *intel_crtc_state = + to_intel_crtc_state(crtc_state); + + if (plane_state->crtc != crtc) + continue; + + if (!intel_fbc_can_choose(to_intel_crtc(crtc))) + break; + + intel_crtc_state->enable_fbc = true; + goto out; } } - mutex_unlock(&dev_priv->fbc.lock); +out: + mutex_unlock(&fbc->lock); } /** * intel_fbc_enable: tries to enable FBC on the CRTC * @crtc: the CRTC * - * This function checks if it's possible to enable FBC on the following CRTC, - * then enables it. Notice that it doesn't activate FBC. + * This function checks if the given CRTC was chosen for FBC, then enables it if + * possible. Notice that it doesn't activate FBC. It is valid to call + * intel_fbc_enable multiple times for the same pipe without an + * intel_fbc_disable in the middle, as long as it is deactivated. */ void intel_fbc_enable(struct intel_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct intel_fbc *fbc = &dev_priv->fbc; if (!fbc_supported(dev_priv)) return; - mutex_lock(&dev_priv->fbc.lock); - - if (dev_priv->fbc.enabled) { - WARN_ON(dev_priv->fbc.crtc == crtc); - goto out; - } - - WARN_ON(dev_priv->fbc.active); - WARN_ON(dev_priv->fbc.crtc != NULL); + mutex_lock(&fbc->lock); - if (intel_vgpu_active(dev_priv->dev)) { - set_no_fbc_reason(dev_priv, "VGPU is active"); - goto out; - } - - if (i915.enable_fbc < 0) { - set_no_fbc_reason(dev_priv, "disabled per chip default"); + if (fbc->enabled) { + WARN_ON(fbc->crtc == NULL); + if (fbc->crtc == crtc) { + WARN_ON(!crtc->config->enable_fbc); + WARN_ON(fbc->active); + } goto out; } - if (!i915.enable_fbc) { - set_no_fbc_reason(dev_priv, "disabled per module param"); + if (!crtc->config->enable_fbc) goto out; - } - if (!crtc_can_fbc(crtc)) { - set_no_fbc_reason(dev_priv, "no enabled pipes can have FBC"); - goto out; - } + WARN_ON(fbc->active); + WARN_ON(fbc->crtc != NULL); + intel_fbc_update_state_cache(crtc); if (intel_fbc_alloc_cfb(crtc)) { - set_no_fbc_reason(dev_priv, "not enough stolen memory"); + fbc->no_fbc_reason = "not enough stolen memory"; goto out; } DRM_DEBUG_KMS("Enabling FBC on pipe %c\n", pipe_name(crtc->pipe)); - dev_priv->fbc.no_fbc_reason = "FBC enabled but not active yet\n"; + fbc->no_fbc_reason = "FBC enabled but not active yet\n"; - dev_priv->fbc.enabled = true; - dev_priv->fbc.crtc = crtc; + fbc->enabled = true; + fbc->crtc = crtc; out: - mutex_unlock(&dev_priv->fbc.lock); + mutex_unlock(&fbc->lock); } /** @@ -1013,58 +1137,88 @@ out: */ static void __intel_fbc_disable(struct drm_i915_private *dev_priv) { - struct intel_crtc *crtc = dev_priv->fbc.crtc; + struct intel_fbc *fbc = &dev_priv->fbc; + struct intel_crtc *crtc = fbc->crtc; - WARN_ON(!mutex_is_locked(&dev_priv->fbc.lock)); - WARN_ON(!dev_priv->fbc.enabled); - WARN_ON(dev_priv->fbc.active); - assert_pipe_disabled(dev_priv, crtc->pipe); + WARN_ON(!mutex_is_locked(&fbc->lock)); + WARN_ON(!fbc->enabled); + WARN_ON(fbc->active); + WARN_ON(crtc->active); DRM_DEBUG_KMS("Disabling FBC on pipe %c\n", pipe_name(crtc->pipe)); __intel_fbc_cleanup_cfb(dev_priv); - dev_priv->fbc.enabled = false; - dev_priv->fbc.crtc = NULL; + fbc->enabled = false; + fbc->crtc = NULL; } /** - * intel_fbc_disable_crtc - disable FBC if it's associated with crtc + * intel_fbc_disable - disable FBC if it's associated with crtc * @crtc: the CRTC * * This function disables FBC if it's associated with the provided CRTC. */ -void intel_fbc_disable_crtc(struct intel_crtc *crtc) +void intel_fbc_disable(struct intel_crtc *crtc) { struct drm_i915_private *dev_priv = crtc->base.dev->dev_private; + struct intel_fbc *fbc = &dev_priv->fbc; if (!fbc_supported(dev_priv)) return; - mutex_lock(&dev_priv->fbc.lock); - if (dev_priv->fbc.crtc == crtc) { - WARN_ON(!dev_priv->fbc.enabled); - WARN_ON(dev_priv->fbc.active); + mutex_lock(&fbc->lock); + if (fbc->crtc == crtc) { + WARN_ON(!fbc->enabled); + WARN_ON(fbc->active); __intel_fbc_disable(dev_priv); } - mutex_unlock(&dev_priv->fbc.lock); + mutex_unlock(&fbc->lock); + + cancel_work_sync(&fbc->work.work); } /** - * intel_fbc_disable - globally disable FBC + * intel_fbc_global_disable - globally disable FBC * @dev_priv: i915 device instance * * This function disables FBC regardless of which CRTC is associated with it. */ -void intel_fbc_disable(struct drm_i915_private *dev_priv) +void intel_fbc_global_disable(struct drm_i915_private *dev_priv) { + struct intel_fbc *fbc = &dev_priv->fbc; + if (!fbc_supported(dev_priv)) return; - mutex_lock(&dev_priv->fbc.lock); - if (dev_priv->fbc.enabled) + mutex_lock(&fbc->lock); + if (fbc->enabled) __intel_fbc_disable(dev_priv); - mutex_unlock(&dev_priv->fbc.lock); + mutex_unlock(&fbc->lock); + + cancel_work_sync(&fbc->work.work); +} + +/** + * intel_fbc_init_pipe_state - initialize FBC's CRTC visibility tracking + * @dev_priv: i915 device instance + * + * The FBC code needs to track CRTC visibility since the older platforms can't + * have FBC enabled while multiple pipes are used. This function does the + * initial setup at driver load to make sure FBC is matching the real hardware. + */ +void intel_fbc_init_pipe_state(struct drm_i915_private *dev_priv) +{ + struct intel_crtc *crtc; + + /* Don't even bother tracking anything if we don't need. */ + if (!no_fbc_on_multiple_pipes(dev_priv)) + return; + + for_each_intel_crtc(dev_priv->dev, crtc) + if (intel_crtc_active(&crtc->base) && + to_intel_plane_state(crtc->base.primary->state)->visible) + dev_priv->fbc.visible_pipes_mask |= (1 << crtc->pipe); } /** @@ -1075,51 +1229,35 @@ void intel_fbc_disable(struct drm_i915_private *dev_priv) */ void intel_fbc_init(struct drm_i915_private *dev_priv) { + struct intel_fbc *fbc = &dev_priv->fbc; enum pipe pipe; - INIT_WORK(&dev_priv->fbc.work.work, intel_fbc_work_fn); - mutex_init(&dev_priv->fbc.lock); - dev_priv->fbc.enabled = false; - dev_priv->fbc.active = false; - dev_priv->fbc.work.scheduled = false; + INIT_WORK(&fbc->work.work, intel_fbc_work_fn); + mutex_init(&fbc->lock); + fbc->enabled = false; + fbc->active = false; + fbc->work.scheduled = false; if (!HAS_FBC(dev_priv)) { - dev_priv->fbc.no_fbc_reason = "unsupported by this chipset"; + fbc->no_fbc_reason = "unsupported by this chipset"; return; } for_each_pipe(dev_priv, pipe) { - dev_priv->fbc.possible_framebuffer_bits |= + fbc->possible_framebuffer_bits |= INTEL_FRONTBUFFER_PRIMARY(pipe); if (fbc_on_pipe_a_only(dev_priv)) break; } - if (INTEL_INFO(dev_priv)->gen >= 7) { - dev_priv->fbc.is_active = ilk_fbc_is_active; - dev_priv->fbc.activate = gen7_fbc_activate; - dev_priv->fbc.deactivate = ilk_fbc_deactivate; - } else if (INTEL_INFO(dev_priv)->gen >= 5) { - dev_priv->fbc.is_active = ilk_fbc_is_active; - dev_priv->fbc.activate = ilk_fbc_activate; - dev_priv->fbc.deactivate = ilk_fbc_deactivate; - } else if (IS_GM45(dev_priv)) { - dev_priv->fbc.is_active = g4x_fbc_is_active; - dev_priv->fbc.activate = g4x_fbc_activate; - dev_priv->fbc.deactivate = g4x_fbc_deactivate; - } else { - dev_priv->fbc.is_active = i8xx_fbc_is_active; - dev_priv->fbc.activate = i8xx_fbc_activate; - dev_priv->fbc.deactivate = i8xx_fbc_deactivate; - - /* This value was pulled out of someone's hat */ + /* This value was pulled out of someone's hat */ + if (INTEL_INFO(dev_priv)->gen <= 4 && !IS_GM45(dev_priv)) I915_WRITE(FBC_CONTROL, 500 << FBC_CTL_INTERVAL_SHIFT); - } /* We still don't have any sort of hardware state readout for FBC, so * deactivate it in case the BIOS activated it to make sure software * matches the hardware state. */ - if (dev_priv->fbc.is_active(dev_priv)) - dev_priv->fbc.deactivate(dev_priv); + if (intel_fbc_hw_is_active(dev_priv)) + intel_fbc_hw_deactivate(dev_priv); } diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 09840f4380f9..97a91e631915 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -406,8 +406,8 @@ retry: continue; } - encoder = connector->encoder; - if (!encoder || WARN_ON(!encoder->crtc)) { + encoder = connector->state->best_encoder; + if (!encoder || WARN_ON(!connector->state->crtc)) { if (connector->force > DRM_FORCE_OFF) goto bail; @@ -420,7 +420,7 @@ retry: num_connectors_enabled++; - new_crtc = intel_fb_helper_crtc(fb_helper, encoder->crtc); + new_crtc = intel_fb_helper_crtc(fb_helper, connector->state->crtc); /* * Make sure we're not trying to drive multiple connectors @@ -466,17 +466,22 @@ retry: * usually contains. But since our current * code puts a mode derived from the post-pfit timings * into crtc->mode this works out correctly. + * + * This is crtc->mode and not crtc->state->mode for the + * fastboot check to work correctly. crtc_state->mode has + * I915_MODE_FLAG_INHERITED, which we clear to force check + * state. */ DRM_DEBUG_KMS("looking for current mode on connector %s\n", connector->name); - modes[i] = &encoder->crtc->mode; + modes[i] = &connector->state->crtc->mode; } crtcs[i] = new_crtc; DRM_DEBUG_KMS("connector %s on pipe %c [CRTC:%d]: %dx%d%s\n", connector->name, - pipe_name(to_intel_crtc(encoder->crtc)->pipe), - encoder->crtc->base.id, + pipe_name(to_intel_crtc(connector->state->crtc)->pipe), + connector->state->crtc->base.id, modes[i]->hdisplay, modes[i]->vdisplay, modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" :""); diff --git a/drivers/gpu/drm/i915/intel_guc.h b/drivers/gpu/drm/i915/intel_guc.h index 045b1491ff7a..73002e901ff2 100644 --- a/drivers/gpu/drm/i915/intel_guc.h +++ b/drivers/gpu/drm/i915/intel_guc.h @@ -46,7 +46,7 @@ struct i915_guc_client { uint32_t wq_head; /* GuC submission statistics & status */ - uint64_t submissions[I915_NUM_RINGS]; + uint64_t submissions[GUC_MAX_ENGINES_NUM]; uint32_t q_fail; uint32_t b_fail; int retcode; @@ -106,8 +106,8 @@ struct intel_guc { uint32_t action_fail; /* Total number of failures */ int32_t action_err; /* Last error code */ - uint64_t submissions[I915_NUM_RINGS]; - uint32_t last_seqno[I915_NUM_RINGS]; + uint64_t submissions[GUC_MAX_ENGINES_NUM]; + uint32_t last_seqno[GUC_MAX_ENGINES_NUM]; }; /* intel_guc_loader.c */ diff --git a/drivers/gpu/drm/i915/intel_guc_fwif.h b/drivers/gpu/drm/i915/intel_guc_fwif.h index 1856a4740b83..2de57ffe5e18 100644 --- a/drivers/gpu/drm/i915/intel_guc_fwif.h +++ b/drivers/gpu/drm/i915/intel_guc_fwif.h @@ -44,6 +44,13 @@ #define GUC_MAX_GPU_CONTEXTS 1024 #define GUC_INVALID_CTX_ID GUC_MAX_GPU_CONTEXTS +#define GUC_RENDER_ENGINE 0 +#define GUC_VIDEO_ENGINE 1 +#define GUC_BLITTER_ENGINE 2 +#define GUC_VIDEOENHANCE_ENGINE 3 +#define GUC_VIDEO_ENGINE2 4 +#define GUC_MAX_ENGINES_NUM (GUC_VIDEO_ENGINE2 + 1) + /* Work queue item header definitions */ #define WQ_STATUS_ACTIVE 1 #define WQ_STATUS_SUSPENDED 2 @@ -285,7 +292,7 @@ struct guc_context_desc { u64 db_trigger_phy; u16 db_id; - struct guc_execlist_context lrc[I915_NUM_RINGS]; + struct guc_execlist_context lrc[GUC_MAX_ENGINES_NUM]; u8 attribute; @@ -344,7 +351,7 @@ struct guc_policy { } __packed; struct guc_policies { - struct guc_policy policy[GUC_CTX_PRIORITY_NUM][I915_NUM_RINGS]; + struct guc_policy policy[GUC_CTX_PRIORITY_NUM][GUC_MAX_ENGINES_NUM]; /* In micro seconds. How much time to allow before DPC processing is * called back via interrupt (to prevent DPC queue drain starving). @@ -388,14 +395,14 @@ struct guc_mmio_regset { struct guc_mmio_reg_state { struct guc_mmio_regset global_reg; - struct guc_mmio_regset engine_reg[I915_NUM_RINGS]; + struct guc_mmio_regset engine_reg[GUC_MAX_ENGINES_NUM]; /* MMIO registers that are set as non privileged */ struct __packed { u32 mmio_start; u32 offsets[GUC_MMIO_WHITE_LIST_MAX]; u32 count; - } mmio_white_list[I915_NUM_RINGS]; + } mmio_white_list[GUC_MAX_ENGINES_NUM]; } __packed; /* GuC Additional Data Struct */ @@ -406,7 +413,7 @@ struct guc_ads { u32 golden_context_lrca; u32 scheduler_policies; u32 reserved0[3]; - u32 eng_state_size[I915_NUM_RINGS]; + u32 eng_state_size[GUC_MAX_ENGINES_NUM]; u32 reserved2[4]; } __packed; diff --git a/drivers/gpu/drm/i915/intel_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c index 3accd914490f..82a3c03fbc0e 100644 --- a/drivers/gpu/drm/i915/intel_guc_loader.c +++ b/drivers/gpu/drm/i915/intel_guc_loader.c @@ -199,7 +199,7 @@ static void set_guc_init_params(struct drm_i915_private *dev_priv) * the value matches either of two values representing completion * of the GuC boot process. * - * This is used for polling the GuC status in a wait_for_atomic() + * This is used for polling the GuC status in a wait_for() * loop below. */ static inline bool guc_ucode_response(struct drm_i915_private *dev_priv, @@ -259,14 +259,14 @@ static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv) I915_WRITE(DMA_CTRL, _MASKED_BIT_ENABLE(UOS_MOVE | START_DMA)); /* - * Spin-wait for the DMA to complete & the GuC to start up. + * Wait for the DMA to complete & the GuC to start up. * NB: Docs recommend not using the interrupt for completion. * Measurements indicate this should take no more than 20ms, so a * timeout here indicates that the GuC has failed and is unusable. * (Higher levels of the driver will attempt to fall back to * execlist mode if this happens.) */ - ret = wait_for_atomic(guc_ucode_response(dev_priv, &status), 100); + ret = wait_for(guc_ucode_response(dev_priv, &status), 100); DRM_DEBUG_DRIVER("DMA status 0x%x, GuC status 0x%x\n", I915_READ(DMA_CTRL), status); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 8698a643d027..80b44c054087 100755..100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -880,15 +880,18 @@ static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder, struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); enum intel_display_power_domain power_domain; u32 tmp; + bool ret; power_domain = intel_display_port_power_domain(encoder); - if (!intel_display_power_is_enabled(dev_priv, power_domain)) + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) return false; + ret = false; + tmp = I915_READ(intel_hdmi->hdmi_reg); if (!(tmp & SDVO_ENABLE)) - return false; + goto out; if (HAS_PCH_CPT(dev)) *pipe = PORT_TO_PIPE_CPT(tmp); @@ -897,7 +900,12 @@ static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder, else *pipe = PORT_TO_PIPE(tmp); - return true; + ret = true; + +out: + intel_display_power_put(dev_priv, power_domain); + + return ret; } static void intel_hdmi_get_config(struct intel_encoder *encoder, @@ -1202,11 +1210,19 @@ intel_hdmi_mode_valid(struct drm_connector *connector, struct drm_device *dev = intel_hdmi_to_dev(hdmi); enum drm_mode_status status; int clock; + int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; if (mode->flags & DRM_MODE_FLAG_DBLSCAN) return MODE_NO_DBLESCAN; clock = mode->clock; + + if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) + clock *= 2; + + if (clock > max_dotclk) + return MODE_CLOCK_HIGH; + if (mode->flags & DRM_MODE_FLAG_DBLCLK) clock *= 2; diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 25254b5c1ac5..deb8282c26d8 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -683,7 +683,7 @@ int intel_setup_gmbus(struct drm_device *dev) return 0; err: - while (--pin) { + while (pin--) { if (!intel_gmbus_is_valid_pin(dev_priv, pin)) continue; diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 73d4347429df..6a978ce80244 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -223,9 +223,11 @@ enum { FAULT_AND_CONTINUE /* Unsupported */ }; #define GEN8_CTX_ID_SHIFT 32 -#define CTX_RCS_INDIRECT_CTX_OFFSET_DEFAULT 0x17 +#define GEN8_CTX_RCS_INDIRECT_CTX_OFFSET_DEFAULT 0x17 +#define GEN9_CTX_RCS_INDIRECT_CTX_OFFSET_DEFAULT 0x26 -static int intel_lr_context_pin(struct drm_i915_gem_request *rq); +static int intel_lr_context_pin(struct intel_context *ctx, + struct intel_engine_cs *engine); static void lrc_setup_hardware_status_page(struct intel_engine_cs *ring, struct drm_i915_gem_object *default_ctx_obj); @@ -393,7 +395,6 @@ static int execlists_update_context(struct drm_i915_gem_request *rq) uint32_t *reg_state = rq->ctx->engine[ring->id].lrc_reg_state; reg_state[CTX_RING_TAIL+1] = rq->tail; - reg_state[CTX_RING_BUFFER_START+1] = rq->ringbuf->vma->node.start; if (ppgtt && !USES_FULL_48BIT_PPGTT(ppgtt->base.dev)) { /* True 32b PPGTT with dynamic page allocation: update PDP @@ -599,7 +600,7 @@ static int execlists_context_queue(struct drm_i915_gem_request *request) int num_elements = 0; if (request->ctx != request->i915->kernel_context) - intel_lr_context_pin(request); + intel_lr_context_pin(request->ctx, ring); i915_gem_request_reference(request); @@ -704,7 +705,7 @@ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request } if (request->ctx != request->i915->kernel_context) - ret = intel_lr_context_pin(request); + ret = intel_lr_context_pin(request->ctx, request->ring); return ret; } @@ -765,6 +766,7 @@ intel_logical_ring_advance_and_submit(struct drm_i915_gem_request *request) { struct intel_ringbuffer *ringbuf = request->ringbuf; struct drm_i915_private *dev_priv = request->i915; + struct intel_engine_cs *engine = request->ring; intel_logical_ring_advance(ringbuf); request->tail = ringbuf->tail; @@ -779,9 +781,20 @@ intel_logical_ring_advance_and_submit(struct drm_i915_gem_request *request) intel_logical_ring_emit(ringbuf, MI_NOOP); intel_logical_ring_advance(ringbuf); - if (intel_ring_stopped(request->ring)) + if (intel_ring_stopped(engine)) return 0; + if (engine->last_context != request->ctx) { + if (engine->last_context) + intel_lr_context_unpin(engine->last_context, engine); + if (request->ctx != request->i915->kernel_context) { + intel_lr_context_pin(request->ctx, engine); + engine->last_context = request->ctx; + } else { + engine->last_context = NULL; + } + } + if (dev_priv->guc.execbuf_client) i915_guc_submit(dev_priv->guc.execbuf_client, request); else @@ -1015,7 +1028,8 @@ void intel_execlists_retire_requests(struct intel_engine_cs *ring) ctx->engine[ring->id].state; if (ctx_obj && (ctx != req->i915->kernel_context)) - intel_lr_context_unpin(req); + intel_lr_context_unpin(ctx, ring); + list_del(&req->execlist_link); i915_gem_request_unreference(req); } @@ -1059,14 +1073,15 @@ int logical_ring_flush_all_caches(struct drm_i915_gem_request *req) return 0; } -static int intel_lr_context_do_pin(struct intel_engine_cs *ring, - struct intel_context *ctx) +static int intel_lr_context_do_pin(struct intel_context *ctx, + struct intel_engine_cs *ring) { struct drm_device *dev = ring->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *ctx_obj = ctx->engine[ring->id].state; struct intel_ringbuffer *ringbuf = ctx->engine[ring->id].ringbuf; struct page *lrc_state_page; + uint32_t *lrc_reg_state; int ret; WARN_ON(!mutex_is_locked(&ring->dev->struct_mutex)); @@ -1088,7 +1103,9 @@ static int intel_lr_context_do_pin(struct intel_engine_cs *ring, ctx->engine[ring->id].lrc_vma = i915_gem_obj_to_ggtt(ctx_obj); intel_lr_context_descriptor_update(ctx, ring); - ctx->engine[ring->id].lrc_reg_state = kmap(lrc_state_page); + lrc_reg_state = kmap(lrc_state_page); + lrc_reg_state[CTX_RING_BUFFER_START+1] = ringbuf->vma->node.start; + ctx->engine[ring->id].lrc_reg_state = lrc_reg_state; ctx_obj->dirty = true; /* Invalidate GuC TLB. */ @@ -1103,41 +1120,40 @@ unpin_ctx_obj: return ret; } -static int intel_lr_context_pin(struct drm_i915_gem_request *rq) +static int intel_lr_context_pin(struct intel_context *ctx, + struct intel_engine_cs *engine) { int ret = 0; - struct intel_engine_cs *ring = rq->ring; - if (rq->ctx->engine[ring->id].pin_count++ == 0) { - ret = intel_lr_context_do_pin(ring, rq->ctx); + if (ctx->engine[engine->id].pin_count++ == 0) { + ret = intel_lr_context_do_pin(ctx, engine); if (ret) goto reset_pin_count; + + i915_gem_context_reference(ctx); } return ret; reset_pin_count: - rq->ctx->engine[ring->id].pin_count = 0; + ctx->engine[engine->id].pin_count = 0; return ret; } -void intel_lr_context_unpin(struct drm_i915_gem_request *rq) +void intel_lr_context_unpin(struct intel_context *ctx, + struct intel_engine_cs *engine) { - struct intel_engine_cs *ring = rq->ring; - struct drm_i915_gem_object *ctx_obj = rq->ctx->engine[ring->id].state; - struct intel_ringbuffer *ringbuf = rq->ringbuf; + struct drm_i915_gem_object *ctx_obj = ctx->engine[engine->id].state; - WARN_ON(!mutex_is_locked(&ring->dev->struct_mutex)); - - if (!ctx_obj) - return; - - if (--rq->ctx->engine[ring->id].pin_count == 0) { - kunmap(kmap_to_page(rq->ctx->engine[ring->id].lrc_reg_state)); - intel_unpin_ringbuffer_obj(ringbuf); + WARN_ON(!mutex_is_locked(&ctx->i915->dev->struct_mutex)); + if (--ctx->engine[engine->id].pin_count == 0) { + kunmap(kmap_to_page(ctx->engine[engine->id].lrc_reg_state)); + intel_unpin_ringbuffer_obj(ctx->engine[engine->id].ringbuf); i915_gem_object_ggtt_unpin(ctx_obj); - rq->ctx->engine[ring->id].lrc_vma = NULL; - rq->ctx->engine[ring->id].lrc_desc = 0; - rq->ctx->engine[ring->id].lrc_reg_state = NULL; + ctx->engine[engine->id].lrc_vma = NULL; + ctx->engine[engine->id].lrc_desc = 0; + ctx->engine[engine->id].lrc_reg_state = NULL; + + i915_gem_context_unreference(ctx); } } @@ -2062,7 +2078,7 @@ logical_ring_init(struct drm_device *dev, struct intel_engine_cs *ring) goto error; /* As this is the default context, always pin it */ - ret = intel_lr_context_do_pin(ring, dctx); + ret = intel_lr_context_do_pin(dctx, ring); if (ret) { DRM_ERROR( "Failed to pin and map ringbuffer %s: %d\n", @@ -2086,6 +2102,7 @@ static int logical_render_ring_init(struct drm_device *dev) ring->name = "render ring"; ring->id = RCS; ring->exec_id = I915_EXEC_RENDER; + ring->guc_id = GUC_RENDER_ENGINE; ring->mmio_base = RENDER_RING_BASE; logical_ring_default_irqs(ring, GEN8_RCS_IRQ_SHIFT); @@ -2137,6 +2154,7 @@ static int logical_bsd_ring_init(struct drm_device *dev) ring->name = "bsd ring"; ring->id = VCS; ring->exec_id = I915_EXEC_BSD; + ring->guc_id = GUC_VIDEO_ENGINE; ring->mmio_base = GEN6_BSD_RING_BASE; logical_ring_default_irqs(ring, GEN8_VCS1_IRQ_SHIFT); @@ -2153,6 +2171,7 @@ static int logical_bsd2_ring_init(struct drm_device *dev) ring->name = "bsd2 ring"; ring->id = VCS2; ring->exec_id = I915_EXEC_BSD; + ring->guc_id = GUC_VIDEO_ENGINE2; ring->mmio_base = GEN8_BSD2_RING_BASE; logical_ring_default_irqs(ring, GEN8_VCS2_IRQ_SHIFT); @@ -2169,6 +2188,7 @@ static int logical_blt_ring_init(struct drm_device *dev) ring->name = "blitter ring"; ring->id = BCS; ring->exec_id = I915_EXEC_BLT; + ring->guc_id = GUC_BLITTER_ENGINE; ring->mmio_base = BLT_RING_BASE; logical_ring_default_irqs(ring, GEN8_BCS_IRQ_SHIFT); @@ -2185,6 +2205,7 @@ static int logical_vebox_ring_init(struct drm_device *dev) ring->name = "video enhancement ring"; ring->id = VECS; ring->exec_id = I915_EXEC_VEBOX; + ring->guc_id = GUC_VIDEOENHANCE_ENGINE; ring->mmio_base = VEBOX_RING_BASE; logical_ring_default_irqs(ring, GEN8_VECS_IRQ_SHIFT); @@ -2293,6 +2314,27 @@ make_rpcs(struct drm_device *dev) return rpcs; } +static u32 intel_lr_indirect_ctx_offset(struct intel_engine_cs *ring) +{ + u32 indirect_ctx_offset; + + switch (INTEL_INFO(ring->dev)->gen) { + default: + MISSING_CASE(INTEL_INFO(ring->dev)->gen); + /* fall through */ + case 9: + indirect_ctx_offset = + GEN9_CTX_RCS_INDIRECT_CTX_OFFSET_DEFAULT; + break; + case 8: + indirect_ctx_offset = + GEN8_CTX_RCS_INDIRECT_CTX_OFFSET_DEFAULT; + break; + } + + return indirect_ctx_offset; +} + static int populate_lr_context(struct intel_context *ctx, struct drm_i915_gem_object *ctx_obj, struct intel_engine_cs *ring, struct intel_ringbuffer *ringbuf) @@ -2336,7 +2378,8 @@ populate_lr_context(struct intel_context *ctx, struct drm_i915_gem_object *ctx_o ASSIGN_CTX_REG(reg_state, CTX_CONTEXT_CONTROL, RING_CONTEXT_CONTROL(ring), _MASKED_BIT_ENABLE(CTX_CTRL_INHIBIT_SYN_CTX_SWITCH | CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT | - CTX_CTRL_RS_CTX_ENABLE)); + (HAS_RESOURCE_STREAMER(dev) ? + CTX_CTRL_RS_CTX_ENABLE : 0))); ASSIGN_CTX_REG(reg_state, CTX_RING_HEAD, RING_HEAD(ring->mmio_base), 0); ASSIGN_CTX_REG(reg_state, CTX_RING_TAIL, RING_TAIL(ring->mmio_base), 0); /* Ring buffer start address is not known until the buffer is pinned. @@ -2365,7 +2408,7 @@ populate_lr_context(struct intel_context *ctx, struct drm_i915_gem_object *ctx_o (wa_ctx->indirect_ctx.size / CACHELINE_DWORDS); reg_state[CTX_RCS_INDIRECT_CTX_OFFSET+1] = - CTX_RCS_INDIRECT_CTX_OFFSET_DEFAULT << 6; + intel_lr_indirect_ctx_offset(ring) << 6; reg_state[CTX_BB_PER_CTX_PTR+1] = (ggtt_offset + wa_ctx->per_ctx.offset * sizeof(uint32_t)) | diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h index 49af638f6213..e6cda3e225d0 100644 --- a/drivers/gpu/drm/i915/intel_lrc.h +++ b/drivers/gpu/drm/i915/intel_lrc.h @@ -101,7 +101,8 @@ void intel_lr_context_free(struct intel_context *ctx); uint32_t intel_lr_context_size(struct intel_engine_cs *ring); int intel_lr_context_deferred_alloc(struct intel_context *ctx, struct intel_engine_cs *ring); -void intel_lr_context_unpin(struct drm_i915_gem_request *req); +void intel_lr_context_unpin(struct intel_context *ctx, + struct intel_engine_cs *engine); void intel_lr_context_reset(struct drm_device *dev, struct intel_context *ctx); uint64_t intel_lr_context_descriptor(struct intel_context *ctx, diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 811ddf7799f0..30a8403a8f4f 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -76,22 +76,30 @@ static bool intel_lvds_get_hw_state(struct intel_encoder *encoder, struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); enum intel_display_power_domain power_domain; u32 tmp; + bool ret; power_domain = intel_display_port_power_domain(encoder); - if (!intel_display_power_is_enabled(dev_priv, power_domain)) + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) return false; + ret = false; + tmp = I915_READ(lvds_encoder->reg); if (!(tmp & LVDS_PORT_EN)) - return false; + goto out; if (HAS_PCH_CPT(dev)) *pipe = PORT_TO_PIPE_CPT(tmp); else *pipe = PORT_TO_PIPE(tmp); - return true; + ret = true; + +out: + intel_display_power_put(dev_priv, power_domain); + + return ret; } static void intel_lvds_get_config(struct intel_encoder *encoder, diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 20bf854eae8c..347d4df49a9b 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -548,7 +548,7 @@ static const struct intel_watermark_params i845_wm_info = { * intel_calculate_wm - calculate watermark level * @clock_in_khz: pixel clock * @wm: chip FIFO params - * @pixel_size: display pixel size + * @cpp: bytes per pixel * @latency_ns: memory latency for the platform * * Calculate the watermark level (the level at which the display plane will @@ -564,8 +564,7 @@ static const struct intel_watermark_params i845_wm_info = { */ static unsigned long intel_calculate_wm(unsigned long clock_in_khz, const struct intel_watermark_params *wm, - int fifo_size, - int pixel_size, + int fifo_size, int cpp, unsigned long latency_ns) { long entries_required, wm_size; @@ -576,7 +575,7 @@ static unsigned long intel_calculate_wm(unsigned long clock_in_khz, * clocks go from a few thousand to several hundred thousand. * latency is usually a few thousand */ - entries_required = ((clock_in_khz / 1000) * pixel_size * latency_ns) / + entries_required = ((clock_in_khz / 1000) * cpp * latency_ns) / 1000; entries_required = DIV_ROUND_UP(entries_required, wm->cacheline_size); @@ -640,13 +639,13 @@ static void pineview_update_wm(struct drm_crtc *unused_crtc) crtc = single_enabled_crtc(dev); if (crtc) { const struct drm_display_mode *adjusted_mode = &to_intel_crtc(crtc)->config->base.adjusted_mode; - int pixel_size = crtc->primary->state->fb->bits_per_pixel / 8; + int cpp = drm_format_plane_cpp(crtc->primary->state->fb->pixel_format, 0); int clock = adjusted_mode->crtc_clock; /* Display SR */ wm = intel_calculate_wm(clock, &pineview_display_wm, pineview_display_wm.fifo_size, - pixel_size, latency->display_sr); + cpp, latency->display_sr); reg = I915_READ(DSPFW1); reg &= ~DSPFW_SR_MASK; reg |= FW_WM(wm, SR); @@ -656,7 +655,7 @@ static void pineview_update_wm(struct drm_crtc *unused_crtc) /* cursor SR */ wm = intel_calculate_wm(clock, &pineview_cursor_wm, pineview_display_wm.fifo_size, - pixel_size, latency->cursor_sr); + cpp, latency->cursor_sr); reg = I915_READ(DSPFW3); reg &= ~DSPFW_CURSOR_SR_MASK; reg |= FW_WM(wm, CURSOR_SR); @@ -665,7 +664,7 @@ static void pineview_update_wm(struct drm_crtc *unused_crtc) /* Display HPLL off SR */ wm = intel_calculate_wm(clock, &pineview_display_hplloff_wm, pineview_display_hplloff_wm.fifo_size, - pixel_size, latency->display_hpll_disable); + cpp, latency->display_hpll_disable); reg = I915_READ(DSPFW3); reg &= ~DSPFW_HPLL_SR_MASK; reg |= FW_WM(wm, HPLL_SR); @@ -674,7 +673,7 @@ static void pineview_update_wm(struct drm_crtc *unused_crtc) /* cursor HPLL off SR */ wm = intel_calculate_wm(clock, &pineview_cursor_hplloff_wm, pineview_display_hplloff_wm.fifo_size, - pixel_size, latency->cursor_hpll_disable); + cpp, latency->cursor_hpll_disable); reg = I915_READ(DSPFW3); reg &= ~DSPFW_HPLL_CURSOR_MASK; reg |= FW_WM(wm, HPLL_CURSOR); @@ -698,7 +697,7 @@ static bool g4x_compute_wm0(struct drm_device *dev, { struct drm_crtc *crtc; const struct drm_display_mode *adjusted_mode; - int htotal, hdisplay, clock, pixel_size; + int htotal, hdisplay, clock, cpp; int line_time_us, line_count; int entries, tlb_miss; @@ -713,10 +712,10 @@ static bool g4x_compute_wm0(struct drm_device *dev, clock = adjusted_mode->crtc_clock; htotal = adjusted_mode->crtc_htotal; hdisplay = to_intel_crtc(crtc)->config->pipe_src_w; - pixel_size = crtc->primary->state->fb->bits_per_pixel / 8; + cpp = drm_format_plane_cpp(crtc->primary->state->fb->pixel_format, 0); /* Use the small buffer method to calculate plane watermark */ - entries = ((clock * pixel_size / 1000) * display_latency_ns) / 1000; + entries = ((clock * cpp / 1000) * display_latency_ns) / 1000; tlb_miss = display->fifo_size*display->cacheline_size - hdisplay * 8; if (tlb_miss > 0) entries += tlb_miss; @@ -728,7 +727,7 @@ static bool g4x_compute_wm0(struct drm_device *dev, /* Use the large buffer method to calculate cursor watermark */ line_time_us = max(htotal * 1000 / clock, 1); line_count = (cursor_latency_ns / line_time_us + 1000) / 1000; - entries = line_count * crtc->cursor->state->crtc_w * pixel_size; + entries = line_count * crtc->cursor->state->crtc_w * cpp; tlb_miss = cursor->fifo_size*cursor->cacheline_size - hdisplay * 8; if (tlb_miss > 0) entries += tlb_miss; @@ -784,7 +783,7 @@ static bool g4x_compute_srwm(struct drm_device *dev, { struct drm_crtc *crtc; const struct drm_display_mode *adjusted_mode; - int hdisplay, htotal, pixel_size, clock; + int hdisplay, htotal, cpp, clock; unsigned long line_time_us; int line_count, line_size; int small, large; @@ -800,21 +799,21 @@ static bool g4x_compute_srwm(struct drm_device *dev, clock = adjusted_mode->crtc_clock; htotal = adjusted_mode->crtc_htotal; hdisplay = to_intel_crtc(crtc)->config->pipe_src_w; - pixel_size = crtc->primary->state->fb->bits_per_pixel / 8; + cpp = drm_format_plane_cpp(crtc->primary->state->fb->pixel_format, 0); line_time_us = max(htotal * 1000 / clock, 1); line_count = (latency_ns / line_time_us + 1000) / 1000; - line_size = hdisplay * pixel_size; + line_size = hdisplay * cpp; /* Use the minimum of the small and large buffer method for primary */ - small = ((clock * pixel_size / 1000) * latency_ns) / 1000; + small = ((clock * cpp / 1000) * latency_ns) / 1000; large = line_count * line_size; entries = DIV_ROUND_UP(min(small, large), display->cacheline_size); *display_wm = entries + display->guard_size; /* calculate the self-refresh watermark for display cursor */ - entries = line_count * pixel_size * crtc->cursor->state->crtc_w; + entries = line_count * cpp * crtc->cursor->state->crtc_w; entries = DIV_ROUND_UP(entries, cursor->cacheline_size); *cursor_wm = entries + cursor->guard_size; @@ -906,13 +905,13 @@ enum vlv_wm_level { static unsigned int vlv_wm_method2(unsigned int pixel_rate, unsigned int pipe_htotal, unsigned int horiz_pixels, - unsigned int bytes_per_pixel, + unsigned int cpp, unsigned int latency) { unsigned int ret; ret = (latency * pixel_rate) / (pipe_htotal * 10000); - ret = (ret + 1) * horiz_pixels * bytes_per_pixel; + ret = (ret + 1) * horiz_pixels * cpp; ret = DIV_ROUND_UP(ret, 64); return ret; @@ -941,7 +940,7 @@ static uint16_t vlv_compute_wm_level(struct intel_plane *plane, int level) { struct drm_i915_private *dev_priv = to_i915(plane->base.dev); - int clock, htotal, pixel_size, width, wm; + int clock, htotal, cpp, width, wm; if (dev_priv->wm.pri_latency[level] == 0) return USHRT_MAX; @@ -949,7 +948,7 @@ static uint16_t vlv_compute_wm_level(struct intel_plane *plane, if (!state->visible) return 0; - pixel_size = drm_format_plane_cpp(state->base.fb->pixel_format, 0); + cpp = drm_format_plane_cpp(state->base.fb->pixel_format, 0); clock = crtc->config->base.adjusted_mode.crtc_clock; htotal = crtc->config->base.adjusted_mode.crtc_htotal; width = crtc->config->pipe_src_w; @@ -965,7 +964,7 @@ static uint16_t vlv_compute_wm_level(struct intel_plane *plane, */ wm = 63; } else { - wm = vlv_wm_method2(clock, htotal, width, pixel_size, + wm = vlv_wm_method2(clock, htotal, width, cpp, dev_priv->wm.pri_latency[level] * 10); } @@ -1439,7 +1438,7 @@ static void i965_update_wm(struct drm_crtc *unused_crtc) int clock = adjusted_mode->crtc_clock; int htotal = adjusted_mode->crtc_htotal; int hdisplay = to_intel_crtc(crtc)->config->pipe_src_w; - int pixel_size = crtc->primary->state->fb->bits_per_pixel / 8; + int cpp = drm_format_plane_cpp(crtc->primary->state->fb->pixel_format, 0); unsigned long line_time_us; int entries; @@ -1447,7 +1446,7 @@ static void i965_update_wm(struct drm_crtc *unused_crtc) /* Use ns/us then divide to preserve precision */ entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) * - pixel_size * hdisplay; + cpp * hdisplay; entries = DIV_ROUND_UP(entries, I915_FIFO_LINE_SIZE); srwm = I965_FIFO_SIZE - entries; if (srwm < 0) @@ -1457,7 +1456,7 @@ static void i965_update_wm(struct drm_crtc *unused_crtc) entries, srwm); entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) * - pixel_size * crtc->cursor->state->crtc_w; + cpp * crtc->cursor->state->crtc_w; entries = DIV_ROUND_UP(entries, i965_cursor_wm_info.cacheline_size); cursor_sr = i965_cursor_wm_info.fifo_size - @@ -1518,7 +1517,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) crtc = intel_get_crtc_for_plane(dev, 0); if (intel_crtc_active(crtc)) { const struct drm_display_mode *adjusted_mode; - int cpp = crtc->primary->state->fb->bits_per_pixel / 8; + int cpp = drm_format_plane_cpp(crtc->primary->state->fb->pixel_format, 0); if (IS_GEN2(dev)) cpp = 4; @@ -1540,7 +1539,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) crtc = intel_get_crtc_for_plane(dev, 1); if (intel_crtc_active(crtc)) { const struct drm_display_mode *adjusted_mode; - int cpp = crtc->primary->state->fb->bits_per_pixel / 8; + int cpp = drm_format_plane_cpp(crtc->primary->state->fb->pixel_format, 0); if (IS_GEN2(dev)) cpp = 4; @@ -1586,7 +1585,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) int clock = adjusted_mode->crtc_clock; int htotal = adjusted_mode->crtc_htotal; int hdisplay = to_intel_crtc(enabled)->config->pipe_src_w; - int pixel_size = enabled->primary->state->fb->bits_per_pixel / 8; + int cpp = drm_format_plane_cpp(enabled->primary->state->fb->pixel_format, 0); unsigned long line_time_us; int entries; @@ -1594,7 +1593,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc) /* Use ns/us then divide to preserve precision */ entries = (((sr_latency_ns / line_time_us) + 1000) / 1000) * - pixel_size * hdisplay; + cpp * hdisplay; entries = DIV_ROUND_UP(entries, wm_info->cacheline_size); DRM_DEBUG_KMS("self-refresh entries: %d\n", entries); srwm = wm_info->fifo_size - entries; @@ -1685,15 +1684,14 @@ uint32_t ilk_pipe_pixel_rate(const struct intel_crtc_state *pipe_config) } /* latency must be in 0.1us units. */ -static uint32_t ilk_wm_method1(uint32_t pixel_rate, uint8_t bytes_per_pixel, - uint32_t latency) +static uint32_t ilk_wm_method1(uint32_t pixel_rate, uint8_t cpp, uint32_t latency) { uint64_t ret; if (WARN(latency == 0, "Latency value missing\n")) return UINT_MAX; - ret = (uint64_t) pixel_rate * bytes_per_pixel * latency; + ret = (uint64_t) pixel_rate * cpp * latency; ret = DIV_ROUND_UP_ULL(ret, 64 * 10000) + 2; return ret; @@ -1701,7 +1699,7 @@ static uint32_t ilk_wm_method1(uint32_t pixel_rate, uint8_t bytes_per_pixel, /* latency must be in 0.1us units. */ static uint32_t ilk_wm_method2(uint32_t pixel_rate, uint32_t pipe_htotal, - uint32_t horiz_pixels, uint8_t bytes_per_pixel, + uint32_t horiz_pixels, uint8_t cpp, uint32_t latency) { uint32_t ret; @@ -1712,13 +1710,13 @@ static uint32_t ilk_wm_method2(uint32_t pixel_rate, uint32_t pipe_htotal, return UINT_MAX; ret = (latency * pixel_rate) / (pipe_htotal * 10000); - ret = (ret + 1) * horiz_pixels * bytes_per_pixel; + ret = (ret + 1) * horiz_pixels * cpp; ret = DIV_ROUND_UP(ret, 64) + 2; return ret; } static uint32_t ilk_wm_fbc(uint32_t pri_val, uint32_t horiz_pixels, - uint8_t bytes_per_pixel) + uint8_t cpp) { /* * Neither of these should be possible since this function shouldn't be @@ -1726,12 +1724,12 @@ static uint32_t ilk_wm_fbc(uint32_t pri_val, uint32_t horiz_pixels, * extra paranoid to avoid a potential divide-by-zero if we screw up * elsewhere in the driver. */ - if (WARN_ON(!bytes_per_pixel)) + if (WARN_ON(!cpp)) return 0; if (WARN_ON(!horiz_pixels)) return 0; - return DIV_ROUND_UP(pri_val * 64, horiz_pixels * bytes_per_pixel) + 2; + return DIV_ROUND_UP(pri_val * 64, horiz_pixels * cpp) + 2; } struct ilk_wm_maximums { @@ -1750,13 +1748,14 @@ static uint32_t ilk_compute_pri_wm(const struct intel_crtc_state *cstate, uint32_t mem_value, bool is_lp) { - int bpp = pstate->base.fb ? pstate->base.fb->bits_per_pixel / 8 : 0; + int cpp = pstate->base.fb ? + drm_format_plane_cpp(pstate->base.fb->pixel_format, 0) : 0; uint32_t method1, method2; if (!cstate->base.active || !pstate->visible) return 0; - method1 = ilk_wm_method1(ilk_pipe_pixel_rate(cstate), bpp, mem_value); + method1 = ilk_wm_method1(ilk_pipe_pixel_rate(cstate), cpp, mem_value); if (!is_lp) return method1; @@ -1764,8 +1763,7 @@ static uint32_t ilk_compute_pri_wm(const struct intel_crtc_state *cstate, method2 = ilk_wm_method2(ilk_pipe_pixel_rate(cstate), cstate->base.adjusted_mode.crtc_htotal, drm_rect_width(&pstate->dst), - bpp, - mem_value); + cpp, mem_value); return min(method1, method2); } @@ -1778,18 +1776,18 @@ static uint32_t ilk_compute_spr_wm(const struct intel_crtc_state *cstate, const struct intel_plane_state *pstate, uint32_t mem_value) { - int bpp = pstate->base.fb ? pstate->base.fb->bits_per_pixel / 8 : 0; + int cpp = pstate->base.fb ? + drm_format_plane_cpp(pstate->base.fb->pixel_format, 0) : 0; uint32_t method1, method2; if (!cstate->base.active || !pstate->visible) return 0; - method1 = ilk_wm_method1(ilk_pipe_pixel_rate(cstate), bpp, mem_value); + method1 = ilk_wm_method1(ilk_pipe_pixel_rate(cstate), cpp, mem_value); method2 = ilk_wm_method2(ilk_pipe_pixel_rate(cstate), cstate->base.adjusted_mode.crtc_htotal, drm_rect_width(&pstate->dst), - bpp, - mem_value); + cpp, mem_value); return min(method1, method2); } @@ -1801,16 +1799,20 @@ static uint32_t ilk_compute_cur_wm(const struct intel_crtc_state *cstate, const struct intel_plane_state *pstate, uint32_t mem_value) { - int bpp = pstate->base.fb ? pstate->base.fb->bits_per_pixel / 8 : 0; + /* + * We treat the cursor plane as always-on for the purposes of watermark + * calculation. Until we have two-stage watermark programming merged, + * this is necessary to avoid flickering. + */ + int cpp = 4; + int width = pstate->visible ? pstate->base.crtc_w : 64; - if (!cstate->base.active || !pstate->visible) + if (!cstate->base.active) return 0; return ilk_wm_method2(ilk_pipe_pixel_rate(cstate), cstate->base.adjusted_mode.crtc_htotal, - drm_rect_width(&pstate->dst), - bpp, - mem_value); + width, cpp, mem_value); } /* Only for WM_LP. */ @@ -1818,12 +1820,13 @@ static uint32_t ilk_compute_fbc_wm(const struct intel_crtc_state *cstate, const struct intel_plane_state *pstate, uint32_t pri_val) { - int bpp = pstate->base.fb ? pstate->base.fb->bits_per_pixel / 8 : 0; + int cpp = pstate->base.fb ? + drm_format_plane_cpp(pstate->base.fb->pixel_format, 0) : 0; if (!cstate->base.active || !pstate->visible) return 0; - return ilk_wm_fbc(pri_val, drm_rect_width(&pstate->dst), bpp); + return ilk_wm_fbc(pri_val, drm_rect_width(&pstate->dst), cpp); } static unsigned int ilk_display_fifo_size(const struct drm_device *dev) @@ -2848,7 +2851,10 @@ void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv, memset(ddb, 0, sizeof(*ddb)); for_each_pipe(dev_priv, pipe) { - if (!intel_display_power_is_enabled(dev_priv, POWER_DOMAIN_PIPE(pipe))) + enum intel_display_power_domain power_domain; + + power_domain = POWER_DOMAIN_PIPE(pipe); + if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) continue; for_each_plane(dev_priv, pipe, plane) { @@ -2860,6 +2866,8 @@ void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv, val = I915_READ(CUR_BUF_CFG(pipe)); skl_ddb_entry_init_from_hw(&ddb->plane[pipe][PLANE_CURSOR], val); + + intel_display_power_put(dev_priv, power_domain); } } @@ -3042,26 +3050,25 @@ static uint32_t skl_pipe_pixel_rate(const struct intel_crtc_state *config) /* * The max latency should be 257 (max the punit can code is 255 and we add 2us - * for the read latency) and bytes_per_pixel should always be <= 8, so that + * for the read latency) and cpp should always be <= 8, so that * should allow pixel_rate up to ~2 GHz which seems sufficient since max * 2xcdclk is 1350 MHz and the pixel rate should never exceed that. */ -static uint32_t skl_wm_method1(uint32_t pixel_rate, uint8_t bytes_per_pixel, - uint32_t latency) +static uint32_t skl_wm_method1(uint32_t pixel_rate, uint8_t cpp, uint32_t latency) { uint32_t wm_intermediate_val, ret; if (latency == 0) return UINT_MAX; - wm_intermediate_val = latency * pixel_rate * bytes_per_pixel / 512; + wm_intermediate_val = latency * pixel_rate * cpp / 512; ret = DIV_ROUND_UP(wm_intermediate_val, 1000); return ret; } static uint32_t skl_wm_method2(uint32_t pixel_rate, uint32_t pipe_htotal, - uint32_t horiz_pixels, uint8_t bytes_per_pixel, + uint32_t horiz_pixels, uint8_t cpp, uint64_t tiling, uint32_t latency) { uint32_t ret; @@ -3071,7 +3078,7 @@ static uint32_t skl_wm_method2(uint32_t pixel_rate, uint32_t pipe_htotal, if (latency == 0) return UINT_MAX; - plane_bytes_per_line = horiz_pixels * bytes_per_pixel; + plane_bytes_per_line = horiz_pixels * cpp; if (tiling == I915_FORMAT_MOD_Y_TILED || tiling == I915_FORMAT_MOD_Yf_TILED) { @@ -3121,23 +3128,21 @@ static bool skl_compute_plane_wm(const struct drm_i915_private *dev_priv, uint32_t plane_bytes_per_line, plane_blocks_per_line; uint32_t res_blocks, res_lines; uint32_t selected_result; - uint8_t bytes_per_pixel; + uint8_t cpp; if (latency == 0 || !cstate->base.active || !fb) return false; - bytes_per_pixel = drm_format_plane_cpp(fb->pixel_format, 0); + cpp = drm_format_plane_cpp(fb->pixel_format, 0); method1 = skl_wm_method1(skl_pipe_pixel_rate(cstate), - bytes_per_pixel, - latency); + cpp, latency); method2 = skl_wm_method2(skl_pipe_pixel_rate(cstate), cstate->base.adjusted_mode.crtc_htotal, cstate->pipe_src_w, - bytes_per_pixel, - fb->modifier[0], + cpp, fb->modifier[0], latency); - plane_bytes_per_line = cstate->pipe_src_w * bytes_per_pixel; + plane_bytes_per_line = cstate->pipe_src_w * cpp; plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512); if (fb->modifier[0] == I915_FORMAT_MOD_Y_TILED || @@ -3145,11 +3150,11 @@ static bool skl_compute_plane_wm(const struct drm_i915_private *dev_priv, uint32_t min_scanlines = 4; uint32_t y_tile_minimum; if (intel_rotation_90_or_270(plane->state->rotation)) { - int bpp = (fb->pixel_format == DRM_FORMAT_NV12) ? + int cpp = (fb->pixel_format == DRM_FORMAT_NV12) ? drm_format_plane_cpp(fb->pixel_format, 1) : drm_format_plane_cpp(fb->pixel_format, 0); - switch (bpp) { + switch (cpp) { case 1: min_scanlines = 16; break; @@ -4116,11 +4121,13 @@ bool ironlake_set_drps(struct drm_device *dev, u8 val) static void ironlake_enable_drps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 rgvmodectl = I915_READ(MEMMODECTL); + u32 rgvmodectl; u8 fmax, fmin, fstart, vstart; spin_lock_irq(&mchdev_lock); + rgvmodectl = I915_READ(MEMMODECTL); + /* Enable temp reporting */ I915_WRITE16(PMMISC, I915_READ(PMMISC) | MCPPCE_EN); I915_WRITE16(TSC1, I915_READ(TSC1) | TSE); @@ -4562,12 +4569,62 @@ static void intel_print_rc6_info(struct drm_device *dev, u32 mode) onoff(mode & GEN6_RC_CTL_RC6_ENABLE)); } -static int sanitize_rc6_option(const struct drm_device *dev, int enable_rc6) +static bool bxt_check_bios_rc6_setup(const struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + bool enable_rc6 = true; + unsigned long rc6_ctx_base; + + if (!(I915_READ(RC6_LOCATION) & RC6_CTX_IN_DRAM)) { + DRM_DEBUG_KMS("RC6 Base location not set properly.\n"); + enable_rc6 = false; + } + + /* + * The exact context size is not known for BXT, so assume a page size + * for this check. + */ + rc6_ctx_base = I915_READ(RC6_CTX_BASE) & RC6_CTX_BASE_MASK; + if (!((rc6_ctx_base >= dev_priv->gtt.stolen_reserved_base) && + (rc6_ctx_base + PAGE_SIZE <= dev_priv->gtt.stolen_reserved_base + + dev_priv->gtt.stolen_reserved_size))) { + DRM_DEBUG_KMS("RC6 Base address not as expected.\n"); + enable_rc6 = false; + } + + if (!(((I915_READ(PWRCTX_MAXCNT_RCSUNIT) & IDLE_TIME_MASK) > 1) && + ((I915_READ(PWRCTX_MAXCNT_VCSUNIT0) & IDLE_TIME_MASK) > 1) && + ((I915_READ(PWRCTX_MAXCNT_BCSUNIT) & IDLE_TIME_MASK) > 1) && + ((I915_READ(PWRCTX_MAXCNT_VECSUNIT) & IDLE_TIME_MASK) > 1))) { + DRM_DEBUG_KMS("Engine Idle wait time not set properly.\n"); + enable_rc6 = false; + } + + if (!(I915_READ(GEN6_RC_CONTROL) & (GEN6_RC_CTL_RC6_ENABLE | + GEN6_RC_CTL_HW_ENABLE)) && + ((I915_READ(GEN6_RC_CONTROL) & GEN6_RC_CTL_HW_ENABLE) || + !(I915_READ(GEN6_RC_STATE) & RC6_STATE))) { + DRM_DEBUG_KMS("HW/SW RC6 is not enabled by BIOS.\n"); + enable_rc6 = false; + } + + return enable_rc6; +} + +int sanitize_rc6_option(const struct drm_device *dev, int enable_rc6) { /* No RC6 before Ironlake and code is gone for ilk. */ if (INTEL_INFO(dev)->gen < 6) return 0; + if (!enable_rc6) + return 0; + + if (IS_BROXTON(dev) && !bxt_check_bios_rc6_setup(dev)) { + DRM_INFO("RC6 disabled by BIOS\n"); + return 0; + } + /* Respect the kernel parameter if it is set */ if (enable_rc6 >= 0) { int mask; @@ -5179,8 +5236,6 @@ static void cherryview_setup_pctx(struct drm_device *dev) u32 pcbr; int pctx_size = 32*1024; - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - pcbr = I915_READ(VLV_PCBR); if ((pcbr >> VLV_PCBR_ADDR_SHIFT) == 0) { DRM_DEBUG_DRIVER("BIOS didn't set up PCBR, fixing up\n"); @@ -5202,7 +5257,7 @@ static void valleyview_setup_pctx(struct drm_device *dev) u32 pcbr; int pctx_size = 24*1024; - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + mutex_lock(&dev->struct_mutex); pcbr = I915_READ(VLV_PCBR); if (pcbr) { @@ -5230,7 +5285,7 @@ static void valleyview_setup_pctx(struct drm_device *dev) pctx = i915_gem_object_create_stolen(dev, pctx_size); if (!pctx) { DRM_DEBUG("not enough stolen space for PCTX, disabling\n"); - return; + goto out; } pctx_paddr = dev_priv->mm.stolen_base + pctx->stolen->start; @@ -5239,6 +5294,7 @@ static void valleyview_setup_pctx(struct drm_device *dev) out: DRM_DEBUG_DRIVER("PCBR: 0x%08x\n", I915_READ(VLV_PCBR)); dev_priv->vlv_pctx = pctx; + mutex_unlock(&dev->struct_mutex); } static void valleyview_cleanup_pctx(struct drm_device *dev) @@ -5248,7 +5304,7 @@ static void valleyview_cleanup_pctx(struct drm_device *dev) if (WARN_ON(!dev_priv->vlv_pctx)) return; - drm_gem_object_unreference(&dev_priv->vlv_pctx->base); + drm_gem_object_unreference_unlocked(&dev_priv->vlv_pctx->base); dev_priv->vlv_pctx = NULL; } @@ -6057,7 +6113,6 @@ void intel_init_gt_powersave(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - i915.enable_rc6 = sanitize_rc6_option(dev, i915.enable_rc6); /* * RPM depends on RC6 to save restore the GT HW context, so make RC6 a * requirement. @@ -6192,8 +6247,8 @@ void intel_enable_gt_powersave(struct drm_device *dev) return; if (IS_IRONLAKE_M(dev)) { - mutex_lock(&dev->struct_mutex); ironlake_enable_drps(dev); + mutex_lock(&dev->struct_mutex); intel_init_emon(dev); mutex_unlock(&dev->struct_mutex); } else if (INTEL_INFO(dev)->gen >= 6) { @@ -7189,9 +7244,10 @@ static int chv_gpu_freq(struct drm_i915_private *dev_priv, int val) { int div, czclk_freq = DIV_ROUND_CLOSEST(dev_priv->czclk_freq, 1000); - div = vlv_gpu_freq_div(czclk_freq) / 2; + div = vlv_gpu_freq_div(czclk_freq); if (div < 0) return div; + div /= 2; return DIV_ROUND_CLOSEST(czclk_freq * val, 2 * div) / 2; } @@ -7200,9 +7256,10 @@ static int chv_freq_opcode(struct drm_i915_private *dev_priv, int val) { int mul, czclk_freq = DIV_ROUND_CLOSEST(dev_priv->czclk_freq, 1000); - mul = vlv_gpu_freq_div(czclk_freq) / 2; + mul = vlv_gpu_freq_div(czclk_freq); if (mul < 0) return mul; + mul /= 2; /* CHV needs even values */ return DIV_ROUND_CLOSEST(val * 2 * mul, czclk_freq) * 2; diff --git a/drivers/gpu/drm/i915/intel_psr.c b/drivers/gpu/drm/i915/intel_psr.c index 9ccff3011523..0b42ada338c8 100644 --- a/drivers/gpu/drm/i915/intel_psr.c +++ b/drivers/gpu/drm/i915/intel_psr.c @@ -225,7 +225,12 @@ static void hsw_psr_enable_sink(struct intel_dp *intel_dp) (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT)); } - drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG, DP_PSR_ENABLE); + if (dev_priv->psr.link_standby) + drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG, + DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE); + else + drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG, + DP_PSR_ENABLE); } static void vlv_psr_enable_source(struct intel_dp *intel_dp) @@ -280,6 +285,9 @@ static void hsw_psr_enable_source(struct intel_dp *intel_dp) if (IS_HASWELL(dev)) val |= EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES; + if (dev_priv->psr.link_standby) + val |= EDP_PSR_LINK_STANDBY; + I915_WRITE(EDP_PSR_CTL, val | max_sleep_time << EDP_PSR_MAX_SLEEP_TIME_SHIFT | idle_frames << EDP_PSR_IDLE_FRAME_SHIFT | @@ -304,8 +312,15 @@ static bool intel_psr_match_conditions(struct intel_dp *intel_dp) dev_priv->psr.source_ok = false; - if (IS_HASWELL(dev) && dig_port->port != PORT_A) { - DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n"); + /* + * HSW spec explicitly says PSR is tied to port A. + * BDW+ platforms with DDI implementation of PSR have different + * PSR registers per transcoder and we only implement transcoder EDP + * ones. Since by Display design transcoder EDP is tied to port A + * we can safely escape based on the port A. + */ + if (HAS_DDI(dev) && dig_port->port != PORT_A) { + DRM_DEBUG_KMS("PSR condition failed: Port not supported\n"); return false; } @@ -314,6 +329,12 @@ static bool intel_psr_match_conditions(struct intel_dp *intel_dp) return false; } + if ((IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) && + !dev_priv->psr.link_standby) { + DRM_ERROR("PSR condition failed: Link off requested but not supported on this platform\n"); + return false; + } + if (IS_HASWELL(dev) && I915_READ(HSW_STEREO_3D_CTL(intel_crtc->config->cpu_transcoder)) & S3D_ENABLE) { @@ -327,12 +348,6 @@ static bool intel_psr_match_conditions(struct intel_dp *intel_dp) return false; } - if (!IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev) && - ((dev_priv->vbt.psr.full_link) || (dig_port->port != PORT_A))) { - DRM_DEBUG_KMS("PSR condition failed: Link Standby requested/needed but not supported on this platform\n"); - return false; - } - dev_priv->psr.source_ok = true; return true; } @@ -763,6 +778,36 @@ void intel_psr_init(struct drm_device *dev) dev_priv->psr_mmio_base = IS_HASWELL(dev_priv) ? HSW_EDP_PSR_BASE : BDW_EDP_PSR_BASE; + /* Per platform default */ + if (i915.enable_psr == -1) { + if (IS_HASWELL(dev) || IS_BROADWELL(dev) || + IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) + i915.enable_psr = 1; + else + i915.enable_psr = 0; + } + + /* Set link_standby x link_off defaults */ + if (IS_HASWELL(dev) || IS_BROADWELL(dev)) + /* HSW and BDW require workarounds that we don't implement. */ + dev_priv->psr.link_standby = false; + else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) + /* On VLV and CHV only standby mode is supported. */ + dev_priv->psr.link_standby = true; + else + /* For new platforms let's respect VBT back again */ + dev_priv->psr.link_standby = dev_priv->vbt.psr.full_link; + + /* Override link_standby x link_off defaults */ + if (i915.enable_psr == 2 && !dev_priv->psr.link_standby) { + DRM_DEBUG_KMS("PSR: Forcing link standby\n"); + dev_priv->psr.link_standby = true; + } + if (i915.enable_psr == 3 && dev_priv->psr.link_standby) { + DRM_DEBUG_KMS("PSR: Forcing main link off\n"); + dev_priv->psr.link_standby = false; + } + INIT_DELAYED_WORK(&dev_priv->psr.work, intel_psr_work); mutex_init(&dev_priv->psr.lock); } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 9030e2bca0c0..45ce45a5e122 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -746,9 +746,9 @@ static int intel_rcs_ctx_init(struct drm_i915_gem_request *req) ret = i915_gem_render_state_init(req); if (ret) - DRM_ERROR("init render state: %d\n", ret); + return ret; - return ret; + return 0; } static int wa_add(struct drm_i915_private *dev_priv, @@ -789,6 +789,22 @@ static int wa_add(struct drm_i915_private *dev_priv, #define WA_WRITE(addr, val) WA_REG(addr, 0xffffffff, val) +static int wa_ring_whitelist_reg(struct intel_engine_cs *ring, i915_reg_t reg) +{ + struct drm_i915_private *dev_priv = ring->dev->dev_private; + struct i915_workarounds *wa = &dev_priv->workarounds; + const uint32_t index = wa->hw_whitelist_count[ring->id]; + + if (WARN_ON(index >= RING_MAX_NONPRIV_SLOTS)) + return -EINVAL; + + WA_WRITE(RING_FORCE_TO_NONPRIV(ring->mmio_base, index), + i915_mmio_reg_offset(reg)); + wa->hw_whitelist_count[ring->id]++; + + return 0; +} + static int gen8_init_workarounds(struct intel_engine_cs *ring) { struct drm_device *dev = ring->dev; @@ -894,6 +910,7 @@ static int gen9_init_workarounds(struct intel_engine_cs *ring) struct drm_device *dev = ring->dev; struct drm_i915_private *dev_priv = dev->dev_private; uint32_t tmp; + int ret; /* WaEnableLbsSlaRetryTimerDecrement:skl */ I915_WRITE(BDW_SCRATCH1, I915_READ(BDW_SCRATCH1) | @@ -964,6 +981,20 @@ static int gen9_init_workarounds(struct intel_engine_cs *ring) /* WaDisableSTUnitPowerOptimization:skl,bxt */ WA_SET_BIT_MASKED(HALF_SLICE_CHICKEN2, GEN8_ST_PO_DISABLE); + /* WaOCLCoherentLineFlush:skl,bxt */ + I915_WRITE(GEN8_L3SQCREG4, (I915_READ(GEN8_L3SQCREG4) | + GEN8_LQSC_FLUSH_COHERENT_LINES)); + + /* WaEnablePreemptionGranularityControlByUMD:skl,bxt */ + ret= wa_ring_whitelist_reg(ring, GEN8_CS_CHICKEN1); + if (ret) + return ret; + + /* WaAllowUMDToModifyHDCChicken1:skl,bxt */ + ret = wa_ring_whitelist_reg(ring, GEN8_HDC_CHICKEN1); + if (ret) + return ret; + return 0; } @@ -1019,6 +1050,16 @@ static int skl_init_workarounds(struct intel_engine_cs *ring) if (ret) return ret; + /* + * Actual WA is to disable percontext preemption granularity control + * until D0 which is the default case so this is equivalent to + * !WaDisablePerCtxtPreemptionGranularityControl:skl + */ + if (IS_SKL_REVID(dev, SKL_REVID_E0, REVID_FOREVER)) { + I915_WRITE(GEN7_FF_SLICE_CS_CHICKEN1, + _MASKED_BIT_ENABLE(GEN9_FFSC_PERCTX_PREEMPT_CTRL)); + } + if (IS_SKL_REVID(dev, 0, SKL_REVID_D0)) { /* WaDisableChickenBitTSGBarrierAckForFFSliceCS:skl */ I915_WRITE(FF_SLICE_CS_CHICKEN2, @@ -1071,6 +1112,11 @@ static int skl_init_workarounds(struct intel_engine_cs *ring) GEN7_HALF_SLICE_CHICKEN1, GEN7_SBE_SS_CACHE_DISPATCH_PORT_SHARING_DISABLE); + /* WaDisableLSQCROPERFforOCL:skl */ + ret = wa_ring_whitelist_reg(ring, GEN8_L3SQCREG4); + if (ret) + return ret; + return skl_tune_iz_hashing(ring); } @@ -1106,6 +1152,20 @@ static int bxt_init_workarounds(struct intel_engine_cs *ring) GEN7_SBE_SS_CACHE_DISPATCH_PORT_SHARING_DISABLE); } + /* WaDisableObjectLevelPreemptionForTrifanOrPolygon:bxt */ + /* WaDisableObjectLevelPreemptionForInstancedDraw:bxt */ + /* WaDisableObjectLevelPreemtionForInstanceId:bxt */ + /* WaDisableLSQCROPERFforOCL:bxt */ + if (IS_BXT_REVID(dev, 0, BXT_REVID_A1)) { + ret = wa_ring_whitelist_reg(ring, GEN9_CS_DEBUG_MODE1); + if (ret) + return ret; + + ret = wa_ring_whitelist_reg(ring, GEN8_L3SQCREG4); + if (ret) + return ret; + } + return 0; } @@ -1117,6 +1177,7 @@ int init_workarounds_ring(struct intel_engine_cs *ring) WARN_ON(ring->id != RCS); dev_priv->workarounds.count = 0; + dev_priv->workarounds.hw_whitelist_count[RCS] = 0; if (IS_BROADWELL(dev)) return bdw_init_workarounds(ring); @@ -2058,6 +2119,9 @@ int intel_pin_and_map_ringbuffer_obj(struct drm_device *dev, return ret; } + /* Access through the GTT requires the device to be awake. */ + assert_rpm_wakelock_held(dev_priv); + ringbuf->virtual_start = ioremap_wc(dev_priv->gtt.mappable_base + i915_gem_obj_ggtt_offset(obj), ringbuf->size); if (ringbuf->virtual_start == NULL) { diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index b12f2aabd104..566b0ae10ce0 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -158,6 +158,7 @@ struct intel_engine_cs { #define I915_NUM_RINGS 5 #define _VCS(n) (VCS + (n)) unsigned int exec_id; + unsigned int guc_id; u32 mmio_base; struct drm_device *dev; struct intel_ringbuffer *buffer; diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index bbca527184d0..4172e73212cd 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -284,6 +284,13 @@ static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv) 1 << PIPE_C | 1 << PIPE_B); } +static void hsw_power_well_pre_disable(struct drm_i915_private *dev_priv) +{ + if (IS_BROADWELL(dev_priv)) + gen8_irq_power_well_pre_disable(dev_priv, + 1 << PIPE_C | 1 << PIPE_B); +} + static void skl_power_well_post_enable(struct drm_i915_private *dev_priv, struct i915_power_well *power_well) { @@ -309,6 +316,14 @@ static void skl_power_well_post_enable(struct drm_i915_private *dev_priv, } } +static void skl_power_well_pre_disable(struct drm_i915_private *dev_priv, + struct i915_power_well *power_well) +{ + if (power_well->data == SKL_DISP_PW_2) + gen8_irq_power_well_pre_disable(dev_priv, + 1 << PIPE_C | 1 << PIPE_B); +} + static void hsw_set_power_well(struct drm_i915_private *dev_priv, struct i915_power_well *power_well, bool enable) { @@ -334,6 +349,7 @@ static void hsw_set_power_well(struct drm_i915_private *dev_priv, } else { if (enable_requested) { + hsw_power_well_pre_disable(dev_priv); I915_WRITE(HSW_PWR_WELL_DRIVER, 0); POSTING_READ(HSW_PWR_WELL_DRIVER); DRM_DEBUG_KMS("Requesting to disable the power well\n"); @@ -456,20 +472,61 @@ static void assert_can_disable_dc9(struct drm_i915_private *dev_priv) */ } -static void gen9_set_dc_state_debugmask_memory_up( - struct drm_i915_private *dev_priv) +static void gen9_set_dc_state_debugmask(struct drm_i915_private *dev_priv) { - uint32_t val; + uint32_t val, mask; + + mask = DC_STATE_DEBUG_MASK_MEMORY_UP; + + if (IS_BROXTON(dev_priv)) + mask |= DC_STATE_DEBUG_MASK_CORES; /* The below bit doesn't need to be cleared ever afterwards */ val = I915_READ(DC_STATE_DEBUG); - if (!(val & DC_STATE_DEBUG_MASK_MEMORY_UP)) { - val |= DC_STATE_DEBUG_MASK_MEMORY_UP; + if ((val & mask) != mask) { + val |= mask; I915_WRITE(DC_STATE_DEBUG, val); POSTING_READ(DC_STATE_DEBUG); } } +static void gen9_write_dc_state(struct drm_i915_private *dev_priv, + u32 state) +{ + int rewrites = 0; + int rereads = 0; + u32 v; + + I915_WRITE(DC_STATE_EN, state); + + /* It has been observed that disabling the dc6 state sometimes + * doesn't stick and dmc keeps returning old value. Make sure + * the write really sticks enough times and also force rewrite until + * we are confident that state is exactly what we want. + */ + do { + v = I915_READ(DC_STATE_EN); + + if (v != state) { + I915_WRITE(DC_STATE_EN, state); + rewrites++; + rereads = 0; + } else if (rereads++ > 5) { + break; + } + + } while (rewrites < 100); + + if (v != state) + DRM_ERROR("Writing dc state to 0x%x failed, now 0x%x\n", + state, v); + + /* Most of the times we need one retry, avoid spam */ + if (rewrites > 1) + DRM_DEBUG_KMS("Rewrote dc state to 0x%x %d times\n", + state, rewrites); +} + static void gen9_set_dc_state(struct drm_i915_private *dev_priv, uint32_t state) { uint32_t val; @@ -488,16 +545,21 @@ static void gen9_set_dc_state(struct drm_i915_private *dev_priv, uint32_t state) else if (i915.enable_dc == 1 && state > DC_STATE_EN_UPTO_DC5) state = DC_STATE_EN_UPTO_DC5; - if (state & DC_STATE_EN_UPTO_DC5_DC6_MASK) - gen9_set_dc_state_debugmask_memory_up(dev_priv); - val = I915_READ(DC_STATE_EN); DRM_DEBUG_KMS("Setting DC state from %02x to %02x\n", val & mask, state); + + /* Check if DMC is ignoring our DC state requests */ + if ((val & mask) != dev_priv->csr.dc_state) + DRM_ERROR("DC state mismatch (0x%x -> 0x%x)\n", + dev_priv->csr.dc_state, val & mask); + val &= ~mask; val |= state; - I915_WRITE(DC_STATE_EN, val); - POSTING_READ(DC_STATE_EN); + + gen9_write_dc_state(dev_priv, val); + + dev_priv->csr.dc_state = val & mask; } void bxt_enable_dc9(struct drm_i915_private *dev_priv) @@ -663,6 +725,9 @@ static void skl_set_power_well(struct drm_i915_private *dev_priv, state_mask = SKL_POWER_WELL_STATE(power_well->data); is_enabled = tmp & state_mask; + if (!enable && enable_requested) + skl_power_well_pre_disable(dev_priv, power_well); + if (enable) { if (!enable_requested) { WARN((tmp & state_mask) && @@ -941,6 +1006,9 @@ static void vlv_display_power_well_deinit(struct drm_i915_private *dev_priv) valleyview_disable_display_irqs(dev_priv); spin_unlock_irq(&dev_priv->irq_lock); + /* make sure we're done processing display irqs */ + synchronize_irq(dev_priv->dev->irq); + vlv_power_sequencer_reset(dev_priv); } @@ -1435,6 +1503,22 @@ static void chv_pipe_power_well_disable(struct drm_i915_private *dev_priv, chv_set_pipe_power_well(dev_priv, power_well, false); } +static void +__intel_display_power_get_domain(struct drm_i915_private *dev_priv, + enum intel_display_power_domain domain) +{ + struct i915_power_domains *power_domains = &dev_priv->power_domains; + struct i915_power_well *power_well; + int i; + + for_each_power_well(i, power_well, BIT(domain), power_domains) { + if (!power_well->count++) + intel_power_well_enable(dev_priv, power_well); + } + + power_domains->domain_use_count[domain]++; +} + /** * intel_display_power_get - grab a power domain reference * @dev_priv: i915 device instance @@ -1450,24 +1534,53 @@ static void chv_pipe_power_well_disable(struct drm_i915_private *dev_priv, void intel_display_power_get(struct drm_i915_private *dev_priv, enum intel_display_power_domain domain) { - struct i915_power_domains *power_domains; - struct i915_power_well *power_well; - int i; + struct i915_power_domains *power_domains = &dev_priv->power_domains; intel_runtime_pm_get(dev_priv); - power_domains = &dev_priv->power_domains; + mutex_lock(&power_domains->lock); + + __intel_display_power_get_domain(dev_priv, domain); + + mutex_unlock(&power_domains->lock); +} + +/** + * intel_display_power_get_if_enabled - grab a reference for an enabled display power domain + * @dev_priv: i915 device instance + * @domain: power domain to reference + * + * This function grabs a power domain reference for @domain and ensures that the + * power domain and all its parents are powered up. Therefore users should only + * grab a reference to the innermost power domain they need. + * + * Any power domain reference obtained by this function must have a symmetric + * call to intel_display_power_put() to release the reference again. + */ +bool intel_display_power_get_if_enabled(struct drm_i915_private *dev_priv, + enum intel_display_power_domain domain) +{ + struct i915_power_domains *power_domains = &dev_priv->power_domains; + bool is_enabled; + + if (!intel_runtime_pm_get_if_in_use(dev_priv)) + return false; mutex_lock(&power_domains->lock); - for_each_power_well(i, power_well, BIT(domain), power_domains) { - if (!power_well->count++) - intel_power_well_enable(dev_priv, power_well); + if (__intel_display_power_is_enabled(dev_priv, domain)) { + __intel_display_power_get_domain(dev_priv, domain); + is_enabled = true; + } else { + is_enabled = false; } - power_domains->domain_use_count[domain]++; - mutex_unlock(&power_domains->lock); + + if (!is_enabled) + intel_runtime_pm_put(dev_priv); + + return is_enabled; } /** @@ -2028,8 +2141,8 @@ static void skl_display_core_init(struct drm_i915_private *dev_priv, skl_init_cdclk(dev_priv); - if (dev_priv->csr.dmc_payload) - intel_csr_load_program(dev_priv); + if (dev_priv->csr.dmc_payload && intel_csr_load_program(dev_priv)) + gen9_set_dc_state_debugmask(dev_priv); } static void skl_display_core_uninit(struct drm_i915_private *dev_priv) @@ -2239,6 +2352,41 @@ void intel_runtime_pm_get(struct drm_i915_private *dev_priv) } /** + * intel_runtime_pm_get_if_in_use - grab a runtime pm reference if device in use + * @dev_priv: i915 device instance + * + * This function grabs a device-level runtime pm reference if the device is + * already in use and ensures that it is powered up. + * + * Any runtime pm reference obtained by this function must have a symmetric + * call to intel_runtime_pm_put() to release the reference again. + */ +bool intel_runtime_pm_get_if_in_use(struct drm_i915_private *dev_priv) +{ + struct drm_device *dev = dev_priv->dev; + struct device *device = &dev->pdev->dev; + + if (IS_ENABLED(CONFIG_PM)) { + int ret = pm_runtime_get_if_in_use(device); + + /* + * In cases runtime PM is disabled by the RPM core and we get + * an -EINVAL return value we are not supposed to call this + * function, since the power state is undefined. This applies + * atm to the late/early system suspend/resume handlers. + */ + WARN_ON_ONCE(ret < 0); + if (ret <= 0) + return false; + } + + atomic_inc(&dev_priv->pm.wakeref_count); + assert_rpm_wakelock_held(dev_priv); + + return true; +} + +/** * intel_runtime_pm_get_noresume - grab a runtime pm reference * @dev_priv: i915 device instance * diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 2e1da060b0e1..4ecc076c4041 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1527,6 +1527,7 @@ intel_sdvo_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); + int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; if (mode->flags & DRM_MODE_FLAG_DBLSCAN) return MODE_NO_DBLESCAN; @@ -1537,6 +1538,9 @@ intel_sdvo_mode_valid(struct drm_connector *connector, if (intel_sdvo->pixel_clock_max < mode->clock) return MODE_CLOCK_HIGH; + if (mode->clock > max_dotclk) + return MODE_CLOCK_HIGH; + if (intel_sdvo->is_lvds) { if (mode->hdisplay > intel_sdvo->sdvo_lvds_fixed_mode->hdisplay) return MODE_PANEL; diff --git a/drivers/gpu/drm/i915/intel_sideband.c b/drivers/gpu/drm/i915/intel_sideband.c index 8831fc579ade..c3998188cf35 100644 --- a/drivers/gpu/drm/i915/intel_sideband.c +++ b/drivers/gpu/drm/i915/intel_sideband.c @@ -129,17 +129,18 @@ u32 vlv_nc_read(struct drm_i915_private *dev_priv, u8 addr) return val; } -u32 vlv_gpio_nc_read(struct drm_i915_private *dev_priv, u32 reg) +u32 vlv_iosf_sb_read(struct drm_i915_private *dev_priv, u8 port, u32 reg) { u32 val = 0; - vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_GPIO_NC, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), port, SB_CRRDDA_NP, reg, &val); return val; } -void vlv_gpio_nc_write(struct drm_i915_private *dev_priv, u32 reg, u32 val) +void vlv_iosf_sb_write(struct drm_i915_private *dev_priv, + u8 port, u32 reg, u32 val) { - vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_GPIO_NC, + vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), port, SB_CRWRDA_NP, reg, &val); } @@ -171,20 +172,6 @@ void vlv_ccu_write(struct drm_i915_private *dev_priv, u32 reg, u32 val) SB_CRWRDA_NP, reg, &val); } -u32 vlv_gps_core_read(struct drm_i915_private *dev_priv, u32 reg) -{ - u32 val = 0; - vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_GPS_CORE, - SB_CRRDDA_NP, reg, &val); - return val; -} - -void vlv_gps_core_write(struct drm_i915_private *dev_priv, u32 reg, u32 val) -{ - vlv_sideband_rw(dev_priv, PCI_DEVFN(0, 0), IOSF_PORT_GPS_CORE, - SB_CRWRDA_NP, reg, &val); -} - u32 vlv_dpio_read(struct drm_i915_private *dev_priv, enum pipe pipe, int reg) { u32 val = 0; diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index 0875c8e0ec0a..a2582c455b36 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -350,8 +350,8 @@ vlv_update_plane(struct drm_plane *dplane, int pipe = intel_plane->pipe; int plane = intel_plane->plane; u32 sprctl; - unsigned long sprsurf_offset, linear_offset; - int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + u32 sprsurf_offset, linear_offset; + int cpp = drm_format_plane_cpp(fb->pixel_format, 0); const struct drm_intel_sprite_colorkey *key = &plane_state->ckey; int crtc_x = plane_state->dst.x1; int crtc_y = plane_state->dst.y1; @@ -422,10 +422,9 @@ vlv_update_plane(struct drm_plane *dplane, crtc_w--; crtc_h--; - linear_offset = y * fb->pitches[0] + x * pixel_size; + linear_offset = y * fb->pitches[0] + x * cpp; sprsurf_offset = intel_compute_tile_offset(dev_priv, &x, &y, - fb->modifier[0], - pixel_size, + fb->modifier[0], cpp, fb->pitches[0]); linear_offset -= sprsurf_offset; @@ -434,7 +433,7 @@ vlv_update_plane(struct drm_plane *dplane, x += src_w; y += src_h; - linear_offset += src_h * fb->pitches[0] + src_w * pixel_size; + linear_offset += src_h * fb->pitches[0] + src_w * cpp; } if (key->flags) { @@ -493,8 +492,8 @@ ivb_update_plane(struct drm_plane *plane, struct drm_i915_gem_object *obj = intel_fb_obj(fb); enum pipe pipe = intel_plane->pipe; u32 sprctl, sprscale = 0; - unsigned long sprsurf_offset, linear_offset; - int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + u32 sprsurf_offset, linear_offset; + int cpp = drm_format_plane_cpp(fb->pixel_format, 0); const struct drm_intel_sprite_colorkey *key = &plane_state->ckey; int crtc_x = plane_state->dst.x1; int crtc_y = plane_state->dst.y1; @@ -556,10 +555,9 @@ ivb_update_plane(struct drm_plane *plane, if (crtc_w != src_w || crtc_h != src_h) sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h; - linear_offset = y * fb->pitches[0] + x * pixel_size; + linear_offset = y * fb->pitches[0] + x * cpp; sprsurf_offset = intel_compute_tile_offset(dev_priv, &x, &y, - fb->modifier[0], - pixel_size, + fb->modifier[0], cpp, fb->pitches[0]); linear_offset -= sprsurf_offset; @@ -570,8 +568,7 @@ ivb_update_plane(struct drm_plane *plane, if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) { x += src_w; y += src_h; - linear_offset += src_h * fb->pitches[0] + - src_w * pixel_size; + linear_offset += src_h * fb->pitches[0] + src_w * cpp; } } @@ -635,9 +632,9 @@ ilk_update_plane(struct drm_plane *plane, struct drm_framebuffer *fb = plane_state->base.fb; struct drm_i915_gem_object *obj = intel_fb_obj(fb); int pipe = intel_plane->pipe; - unsigned long dvssurf_offset, linear_offset; u32 dvscntr, dvsscale; - int pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); + u32 dvssurf_offset, linear_offset; + int cpp = drm_format_plane_cpp(fb->pixel_format, 0); const struct drm_intel_sprite_colorkey *key = &plane_state->ckey; int crtc_x = plane_state->dst.x1; int crtc_y = plane_state->dst.y1; @@ -695,10 +692,9 @@ ilk_update_plane(struct drm_plane *plane, if (crtc_w != src_w || crtc_h != src_h) dvsscale = DVS_SCALE_ENABLE | (src_w << 16) | src_h; - linear_offset = y * fb->pitches[0] + x * pixel_size; + linear_offset = y * fb->pitches[0] + x * cpp; dvssurf_offset = intel_compute_tile_offset(dev_priv, &x, &y, - fb->modifier[0], - pixel_size, + fb->modifier[0], cpp, fb->pitches[0]); linear_offset -= dvssurf_offset; @@ -707,7 +703,7 @@ ilk_update_plane(struct drm_plane *plane, x += src_w; y += src_h; - linear_offset += src_h * fb->pitches[0] + src_w * pixel_size; + linear_offset += src_h * fb->pitches[0] + src_w * cpp; } if (key->flags) { @@ -772,7 +768,6 @@ intel_check_sprite_plane(struct drm_plane *plane, int hscale, vscale; int max_scale, min_scale; bool can_scale; - int pixel_size; if (!fb) { state->visible = false; @@ -894,6 +889,7 @@ intel_check_sprite_plane(struct drm_plane *plane, /* Check size restrictions when scaling */ if (state->visible && (src_w != crtc_w || src_h != crtc_h)) { unsigned int width_bytes; + int cpp = drm_format_plane_cpp(fb->pixel_format, 0); WARN_ON(!can_scale); @@ -905,9 +901,7 @@ intel_check_sprite_plane(struct drm_plane *plane, if (src_w < 3 || src_h < 3) state->visible = false; - pixel_size = drm_format_plane_cpp(fb->pixel_format, 0); - width_bytes = ((src_x * pixel_size) & 63) + - src_w * pixel_size; + width_bytes = ((src_x * cpp) & 63) + src_w * cpp; if (INTEL_INFO(dev)->gen < 9 && (src_w > 2048 || src_h > 2048 || width_bytes > 4096 || fb->pitches[0] > 4096)) { diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 948cbff6c62e..6745bad5bff0 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -897,6 +897,10 @@ intel_tv_mode_valid(struct drm_connector *connector, { struct intel_tv *intel_tv = intel_attached_tv(connector); const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); + int max_dotclk = to_i915(connector->dev)->max_dotclk_freq; + + if (mode->clock > max_dotclk) + return MODE_CLOCK_HIGH; /* Ensure TV refresh is close to desired refresh */ if (tv_mode && abs(tv_mode->refresh - drm_mode_vrefresh(mode) * 1000) @@ -1178,10 +1182,9 @@ static int intel_tv_detect_type(struct intel_tv *intel_tv, struct drm_connector *connector) { - struct drm_encoder *encoder = &intel_tv->base.base; - struct drm_crtc *crtc = encoder->crtc; + struct drm_crtc *crtc = connector->state->crtc; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct drm_device *dev = encoder->dev; + struct drm_device *dev = connector->dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 tv_ctl, save_tv_ctl; u32 tv_dac, save_tv_dac; @@ -1230,8 +1233,7 @@ intel_tv_detect_type(struct intel_tv *intel_tv, I915_WRITE(TV_DAC, tv_dac); POSTING_READ(TV_DAC); - intel_wait_for_vblank(intel_tv->base.base.dev, - to_intel_crtc(intel_tv->base.base.crtc)->pipe); + intel_wait_for_vblank(dev, intel_crtc->pipe); type = -1; tv_dac = I915_READ(TV_DAC); @@ -1261,8 +1263,7 @@ intel_tv_detect_type(struct intel_tv *intel_tv, POSTING_READ(TV_CTL); /* For unknown reasons the hw barfs if we don't do this vblank wait. */ - intel_wait_for_vblank(intel_tv->base.base.dev, - to_intel_crtc(intel_tv->base.base.crtc)->pipe); + intel_wait_for_vblank(dev, intel_crtc->pipe); /* Restore interrupt config */ if (connector->polled & DRM_CONNECTOR_POLL_HPD) { @@ -1420,6 +1421,7 @@ intel_tv_get_modes(struct drm_connector *connector) if (!mode_ptr) continue; strncpy(mode_ptr->name, input->name, DRM_DISPLAY_MODE_LEN); + mode_ptr->name[DRM_DISPLAY_MODE_LEN - 1] = '\0'; mode_ptr->hdisplay = hactive_s; mode_ptr->hsync_start = hactive_s + 1; diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index bfa79e5c214e..436d8f2b8682 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -400,6 +400,8 @@ void intel_uncore_early_sanitize(struct drm_device *dev, bool restore_forcewake) void intel_uncore_sanitize(struct drm_device *dev) { + i915.enable_rc6 = sanitize_rc6_option(dev, i915.enable_rc6); + /* BIOS often leaves RC6 enabled, but disable it for hw init */ intel_disable_gt_powersave(dev); } diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/dw_hdmi-imx.c index 21d615827837..2a95d10e9d92 100644 --- a/drivers/gpu/drm/imx/dw_hdmi-imx.c +++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c @@ -118,7 +118,7 @@ static void dw_hdmi_imx_encoder_mode_set(struct drm_encoder *encoder, static void dw_hdmi_imx_encoder_commit(struct drm_encoder *encoder) { struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder); - int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder); + int mux = drm_of_encoder_active_port_id(hdmi->dev->of_node, encoder); regmap_update_bits(hdmi->regmap, IOMUXC_GPR3, IMX6Q_GPR3_HDMI_MUX_CTL_MASK, diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 7c4d1250e071..9876e0f0c3e1 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -17,7 +17,6 @@ #include <linux/device.h> #include <linux/fb.h> #include <linux/module.h> -#include <linux/of_graph.h> #include <linux/platform_device.h> #include <drm/drmP.h> #include <drm/drm_fb_helper.h> @@ -412,36 +411,6 @@ int imx_drm_encoder_parse_of(struct drm_device *drm, } EXPORT_SYMBOL_GPL(imx_drm_encoder_parse_of); -/* - * @node: device tree node containing encoder input ports - * @encoder: drm_encoder - */ -int imx_drm_encoder_get_mux_id(struct device_node *node, - struct drm_encoder *encoder) -{ - struct imx_drm_crtc *imx_crtc = imx_drm_find_crtc(encoder->crtc); - struct device_node *ep; - struct of_endpoint endpoint; - struct device_node *port; - int ret; - - if (!node || !imx_crtc) - return -EINVAL; - - for_each_endpoint_of_node(node, ep) { - port = of_graph_get_remote_port(ep); - of_node_put(port); - if (port == imx_crtc->crtc->port) { - ret = of_graph_parse_endpoint(ep, &endpoint); - of_node_put(ep); - return ret ? ret : endpoint.port; - } - } - - return -EINVAL; -} -EXPORT_SYMBOL_GPL(imx_drm_encoder_get_mux_id); - static const struct drm_ioctl_desc imx_drm_ioctls[] = { /* none so far */ }; diff --git a/drivers/gpu/drm/imx/imx-drm.h b/drivers/gpu/drm/imx/imx-drm.h index 71cf6d9c714f..b0241b9d1334 100644 --- a/drivers/gpu/drm/imx/imx-drm.h +++ b/drivers/gpu/drm/imx/imx-drm.h @@ -46,8 +46,6 @@ int imx_drm_set_bus_format_pins(struct drm_encoder *encoder, int imx_drm_set_bus_format(struct drm_encoder *encoder, u32 bus_format); -int imx_drm_encoder_get_mux_id(struct device_node *node, - struct drm_encoder *encoder); int imx_drm_encoder_parse_of(struct drm_device *drm, struct drm_encoder *encoder, struct device_node *np); diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index 024d6134b848..a58eee59550a 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -19,6 +19,7 @@ #include <drm/drmP.h> #include <drm/drm_fb_helper.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <linux/mfd/syscon.h> #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> @@ -208,7 +209,7 @@ static void imx_ldb_encoder_commit(struct drm_encoder *encoder) struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder); struct imx_ldb *ldb = imx_ldb_ch->ldb; int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; - int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder); + int mux = drm_of_encoder_active_port_id(imx_ldb_ch->child, encoder); drm_panel_prepare(imx_ldb_ch->panel); @@ -258,7 +259,7 @@ static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder, int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN; unsigned long serial_clk; unsigned long di_clk = mode->clock * 1000; - int mux = imx_drm_encoder_get_mux_id(imx_ldb_ch->child, encoder); + int mux = drm_of_encoder_active_port_id(imx_ldb_ch->child, encoder); if (mode->clock > 170000) { dev_warn(ldb->dev, diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3-crtc.c index 846b5f558897..4bea0ddb3f3e 100644 --- a/drivers/gpu/drm/imx/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3-crtc.c @@ -22,6 +22,8 @@ #include <linux/fb.h> #include <linux/clk.h> #include <linux/errno.h> +#include <linux/reservation.h> +#include <linux/dma-buf.h> #include <drm/drm_gem_cma_helper.h> #include <drm/drm_fb_cma_helper.h> @@ -31,6 +33,23 @@ #define DRIVER_DESC "i.MX IPUv3 Graphics" +enum ipu_flip_status { + IPU_FLIP_NONE, + IPU_FLIP_PENDING, + IPU_FLIP_SUBMITTED, +}; + +struct ipu_flip_work { + struct work_struct unref_work; + struct drm_gem_object *bo; + struct drm_pending_vblank_event *page_flip_event; + struct work_struct fence_work; + struct ipu_crtc *crtc; + struct fence *excl; + unsigned shared_count; + struct fence **shared; +}; + struct ipu_crtc { struct device *dev; struct drm_crtc base; @@ -42,8 +61,9 @@ struct ipu_crtc { struct ipu_dc *dc; struct ipu_di *di; int enabled; - struct drm_pending_vblank_event *page_flip_event; - struct drm_framebuffer *newfb; + enum ipu_flip_status flip_state; + struct workqueue_struct *flip_queue; + struct ipu_flip_work *flip_work; int irq; u32 bus_format; int di_hsync_pin; @@ -102,15 +122,45 @@ static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode) } } +static void ipu_flip_unref_work_func(struct work_struct *__work) +{ + struct ipu_flip_work *work = + container_of(__work, struct ipu_flip_work, unref_work); + + drm_gem_object_unreference_unlocked(work->bo); + kfree(work); +} + +static void ipu_flip_fence_work_func(struct work_struct *__work) +{ + struct ipu_flip_work *work = + container_of(__work, struct ipu_flip_work, fence_work); + int i; + + /* wait for all fences attached to the FB obj to signal */ + if (work->excl) { + fence_wait(work->excl, false); + fence_put(work->excl); + } + for (i = 0; i < work->shared_count; i++) { + fence_wait(work->shared[i], false); + fence_put(work->shared[i]); + } + + work->crtc->flip_state = IPU_FLIP_SUBMITTED; +} + static int ipu_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, uint32_t page_flip_flags) { + struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); + struct ipu_flip_work *flip_work; int ret; - if (ipu_crtc->newfb) + if (ipu_crtc->flip_state != IPU_FLIP_NONE) return -EBUSY; ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc); @@ -121,11 +171,58 @@ static int ipu_page_flip(struct drm_crtc *crtc, return ret; } - ipu_crtc->newfb = fb; - ipu_crtc->page_flip_event = event; - crtc->primary->fb = fb; + flip_work = kzalloc(sizeof *flip_work, GFP_KERNEL); + if (!flip_work) { + ret = -ENOMEM; + goto put_vblank; + } + INIT_WORK(&flip_work->unref_work, ipu_flip_unref_work_func); + flip_work->page_flip_event = event; + + /* get BO backing the old framebuffer and take a reference */ + flip_work->bo = &drm_fb_cma_get_gem_obj(crtc->primary->fb, 0)->base; + drm_gem_object_reference(flip_work->bo); + + ipu_crtc->flip_work = flip_work; + /* + * If the object has a DMABUF attached, we need to wait on its fences + * if there are any. + */ + if (cma_obj->base.dma_buf) { + INIT_WORK(&flip_work->fence_work, ipu_flip_fence_work_func); + flip_work->crtc = ipu_crtc; + + ret = reservation_object_get_fences_rcu( + cma_obj->base.dma_buf->resv, &flip_work->excl, + &flip_work->shared_count, &flip_work->shared); + + if (unlikely(ret)) { + DRM_ERROR("failed to get fences for buffer\n"); + goto free_flip_work; + } + + /* No need to queue the worker if the are no fences */ + if (!flip_work->excl && !flip_work->shared_count) { + ipu_crtc->flip_state = IPU_FLIP_SUBMITTED; + } else { + ipu_crtc->flip_state = IPU_FLIP_PENDING; + queue_work(ipu_crtc->flip_queue, + &flip_work->fence_work); + } + } else { + ipu_crtc->flip_state = IPU_FLIP_SUBMITTED; + } return 0; + +free_flip_work: + drm_gem_object_unreference_unlocked(flip_work->bo); + kfree(flip_work); + ipu_crtc->flip_work = NULL; +put_vblank: + imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc); + + return ret; } static const struct drm_crtc_funcs ipu_crtc_funcs = { @@ -209,12 +306,12 @@ static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc) { unsigned long flags; struct drm_device *drm = ipu_crtc->base.dev; + struct ipu_flip_work *work = ipu_crtc->flip_work; spin_lock_irqsave(&drm->event_lock, flags); - if (ipu_crtc->page_flip_event) + if (work->page_flip_event) drm_crtc_send_vblank_event(&ipu_crtc->base, - ipu_crtc->page_flip_event); - ipu_crtc->page_flip_event = NULL; + work->page_flip_event); imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc); spin_unlock_irqrestore(&drm->event_lock, flags); } @@ -225,13 +322,15 @@ static irqreturn_t ipu_irq_handler(int irq, void *dev_id) imx_drm_handle_vblank(ipu_crtc->imx_crtc); - if (ipu_crtc->newfb) { + if (ipu_crtc->flip_state == IPU_FLIP_SUBMITTED) { struct ipu_plane *plane = ipu_crtc->plane[0]; - ipu_crtc->newfb = NULL; ipu_plane_set_base(plane, ipu_crtc->base.primary->fb, plane->x, plane->y); ipu_crtc_handle_pageflip(ipu_crtc); + queue_work(ipu_crtc->flip_queue, + &ipu_crtc->flip_work->unref_work); + ipu_crtc->flip_state = IPU_FLIP_NONE; } return IRQ_HANDLED; @@ -280,11 +379,18 @@ static const struct drm_crtc_helper_funcs ipu_helper_funcs = { static int ipu_enable_vblank(struct drm_crtc *crtc) { + struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); + + enable_irq(ipu_crtc->irq); + return 0; } static void ipu_disable_vblank(struct drm_crtc *crtc) { + struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc); + + disable_irq_nosync(ipu_crtc->irq); } static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, @@ -395,6 +501,10 @@ static int ipu_crtc_init(struct ipu_crtc *ipu_crtc, dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret); goto err_put_plane_res; } + /* Only enable IRQ when we actually need it to trigger work. */ + disable_irq(ipu_crtc->irq); + + ipu_crtc->flip_queue = create_singlethread_workqueue("ipu-crtc-flip"); return 0; @@ -437,6 +547,7 @@ static void ipu_drm_unbind(struct device *dev, struct device *master, imx_drm_remove_crtc(ipu_crtc->imx_crtc); + destroy_workqueue(ipu_crtc->flip_queue); ipu_plane_put_resources(ipu_crtc->plane[0]); ipu_put_resources(ipu_crtc); } diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index 591ba2f1ae03..d078373c4233 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c @@ -338,7 +338,7 @@ static int ipu_update_plane(struct drm_plane *plane, struct drm_crtc *crtc, } if (crtc != plane->crtc) - dev_info(plane->dev->dev, "crtc change: %p -> %p\n", + dev_dbg(plane->dev->dev, "crtc change: %p -> %p\n", plane->crtc, crtc); plane->crtc = crtc; diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 065ad4138799..ddb4c9d097e4 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -12,6 +12,7 @@ msm-y := \ hdmi/hdmi_connector.o \ hdmi/hdmi_hdcp.o \ hdmi/hdmi_i2c.o \ + hdmi/hdmi_phy.o \ hdmi/hdmi_phy_8960.o \ hdmi/hdmi_phy_8x60.o \ hdmi/hdmi_phy_8x74.o \ @@ -52,6 +53,8 @@ msm-y := \ msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o +msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_pll_8960.o +msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_phy_8996.o msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \ mdp/mdp4/mdp4_dsi_encoder.o \ diff --git a/drivers/gpu/drm/msm/adreno/a2xx.xml.h b/drivers/gpu/drm/msm/adreno/a2xx.xml.h index 9e2aceb4ffe6..fee24297fb92 100644 --- a/drivers/gpu/drm/msm/adreno/a2xx.xml.h +++ b/drivers/gpu/drm/msm/adreno/a2xx.xml.h @@ -9,16 +9,17 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 398 bytes, from 2015-09-24 17:25:31) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) - /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10755 bytes, from 2015-09-14 20:46:55) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14968 bytes, from 2015-05-20 20:12:27) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 67771 bytes, from 2015-09-14 20:46:55) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 63970 bytes, from 2015-09-14 20:50:12) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 11518 bytes, from 2016-02-10 21:03:25) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 16166 bytes, from 2016-02-11 21:20:31) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 83967 bytes, from 2016-02-10 17:07:21) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 109916 bytes, from 2016-02-20 18:44:48) - /home/robclark/src/freedreno/envytools/rnndb/adreno/ocmem.xml ( 1773 bytes, from 2015-09-24 17:30:00) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) +- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/drivers/gpu/drm/msm/adreno/a3xx.xml.h b/drivers/gpu/drm/msm/adreno/a3xx.xml.h index 97dc1c6ec107..27dabd5e57fb 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx.xml.h +++ b/drivers/gpu/drm/msm/adreno/a3xx.xml.h @@ -9,16 +9,17 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 398 bytes, from 2015-09-24 17:25:31) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) - /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10755 bytes, from 2015-09-14 20:46:55) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14968 bytes, from 2015-05-20 20:12:27) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 67771 bytes, from 2015-09-14 20:46:55) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 63970 bytes, from 2015-09-14 20:50:12) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 11518 bytes, from 2016-02-10 21:03:25) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 16166 bytes, from 2016-02-11 21:20:31) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 83967 bytes, from 2016-02-10 17:07:21) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 109916 bytes, from 2016-02-20 18:44:48) - /home/robclark/src/freedreno/envytools/rnndb/adreno/ocmem.xml ( 1773 bytes, from 2015-09-24 17:30:00) -Copyright (C) 2013-2015 by the following authors: +Copyright (C) 2013-2016 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) +- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -111,10 +112,14 @@ enum a3xx_vtx_fmt { VFMT_8_8_SNORM = 53, VFMT_8_8_8_SNORM = 54, VFMT_8_8_8_8_SNORM = 55, - VFMT_10_10_10_2_UINT = 60, - VFMT_10_10_10_2_UNORM = 61, - VFMT_10_10_10_2_SINT = 62, - VFMT_10_10_10_2_SNORM = 63, + VFMT_10_10_10_2_UINT = 56, + VFMT_10_10_10_2_UNORM = 57, + VFMT_10_10_10_2_SINT = 58, + VFMT_10_10_10_2_SNORM = 59, + VFMT_2_10_10_10_UINT = 60, + VFMT_2_10_10_10_UNORM = 61, + VFMT_2_10_10_10_SINT = 62, + VFMT_2_10_10_10_SNORM = 63, }; enum a3xx_tex_fmt { @@ -138,10 +143,12 @@ enum a3xx_tex_fmt { TFMT_DXT1 = 36, TFMT_DXT3 = 37, TFMT_DXT5 = 38, + TFMT_2_10_10_10_UNORM = 40, TFMT_10_10_10_2_UNORM = 41, TFMT_9_9_9_E5_FLOAT = 42, TFMT_11_11_10_FLOAT = 43, TFMT_A8_UNORM = 44, + TFMT_L8_UNORM = 45, TFMT_L8_A8_UNORM = 47, TFMT_8_UNORM = 48, TFMT_8_8_UNORM = 49, @@ -183,6 +190,8 @@ enum a3xx_tex_fmt { TFMT_32_SINT = 92, TFMT_32_32_SINT = 93, TFMT_32_32_32_32_SINT = 95, + TFMT_2_10_10_10_UINT = 96, + TFMT_10_10_10_2_UINT = 97, TFMT_ETC2_RG11_SNORM = 112, TFMT_ETC2_RG11_UNORM = 113, TFMT_ETC2_R11_SNORM = 114, @@ -215,6 +224,9 @@ enum a3xx_color_fmt { RB_R8_UINT = 14, RB_R8_SINT = 15, RB_R10G10B10A2_UNORM = 16, + RB_A2R10G10B10_UNORM = 17, + RB_R10G10B10A2_UINT = 18, + RB_A2R10G10B10_UINT = 19, RB_A8_UNORM = 20, RB_R8_UNORM = 21, RB_R16_FLOAT = 24, @@ -244,30 +256,273 @@ enum a3xx_color_fmt { RB_R32G32B32A32_UINT = 59, }; +enum a3xx_cp_perfcounter_select { + CP_ALWAYS_COUNT = 0, + CP_AHB_PFPTRANS_WAIT = 3, + CP_AHB_NRTTRANS_WAIT = 6, + CP_CSF_NRT_READ_WAIT = 8, + CP_CSF_I1_FIFO_FULL = 9, + CP_CSF_I2_FIFO_FULL = 10, + CP_CSF_ST_FIFO_FULL = 11, + CP_RESERVED_12 = 12, + CP_CSF_RING_ROQ_FULL = 13, + CP_CSF_I1_ROQ_FULL = 14, + CP_CSF_I2_ROQ_FULL = 15, + CP_CSF_ST_ROQ_FULL = 16, + CP_RESERVED_17 = 17, + CP_MIU_TAG_MEM_FULL = 18, + CP_MIU_NRT_WRITE_STALLED = 22, + CP_MIU_NRT_READ_STALLED = 23, + CP_ME_REGS_RB_DONE_FIFO_FULL = 26, + CP_ME_REGS_VS_EVENT_FIFO_FULL = 27, + CP_ME_REGS_PS_EVENT_FIFO_FULL = 28, + CP_ME_REGS_CF_EVENT_FIFO_FULL = 29, + CP_ME_MICRO_RB_STARVED = 30, + CP_AHB_RBBM_DWORD_SENT = 40, + CP_ME_BUSY_CLOCKS = 41, + CP_ME_WAIT_CONTEXT_AVAIL = 42, + CP_PFP_TYPE0_PACKET = 43, + CP_PFP_TYPE3_PACKET = 44, + CP_CSF_RB_WPTR_NEQ_RPTR = 45, + CP_CSF_I1_SIZE_NEQ_ZERO = 46, + CP_CSF_I2_SIZE_NEQ_ZERO = 47, + CP_CSF_RBI1I2_FETCHING = 48, +}; + +enum a3xx_gras_tse_perfcounter_select { + GRAS_TSEPERF_INPUT_PRIM = 0, + GRAS_TSEPERF_INPUT_NULL_PRIM = 1, + GRAS_TSEPERF_TRIVAL_REJ_PRIM = 2, + GRAS_TSEPERF_CLIPPED_PRIM = 3, + GRAS_TSEPERF_NEW_PRIM = 4, + GRAS_TSEPERF_ZERO_AREA_PRIM = 5, + GRAS_TSEPERF_FACENESS_CULLED_PRIM = 6, + GRAS_TSEPERF_ZERO_PIXEL_PRIM = 7, + GRAS_TSEPERF_OUTPUT_NULL_PRIM = 8, + GRAS_TSEPERF_OUTPUT_VISIBLE_PRIM = 9, + GRAS_TSEPERF_PRE_CLIP_PRIM = 10, + GRAS_TSEPERF_POST_CLIP_PRIM = 11, + GRAS_TSEPERF_WORKING_CYCLES = 12, + GRAS_TSEPERF_PC_STARVE = 13, + GRAS_TSERASPERF_STALL = 14, +}; + +enum a3xx_gras_ras_perfcounter_select { + GRAS_RASPERF_16X16_TILES = 0, + GRAS_RASPERF_8X8_TILES = 1, + GRAS_RASPERF_4X4_TILES = 2, + GRAS_RASPERF_WORKING_CYCLES = 3, + GRAS_RASPERF_STALL_CYCLES_BY_RB = 4, + GRAS_RASPERF_STALL_CYCLES_BY_VSC = 5, + GRAS_RASPERF_STARVE_CYCLES_BY_TSE = 6, +}; + +enum a3xx_hlsq_perfcounter_select { + HLSQ_PERF_SP_VS_CONSTANT = 0, + HLSQ_PERF_SP_VS_INSTRUCTIONS = 1, + HLSQ_PERF_SP_FS_CONSTANT = 2, + HLSQ_PERF_SP_FS_INSTRUCTIONS = 3, + HLSQ_PERF_TP_STATE = 4, + HLSQ_PERF_QUADS = 5, + HLSQ_PERF_PIXELS = 6, + HLSQ_PERF_VERTICES = 7, + HLSQ_PERF_FS8_THREADS = 8, + HLSQ_PERF_FS16_THREADS = 9, + HLSQ_PERF_FS32_THREADS = 10, + HLSQ_PERF_VS8_THREADS = 11, + HLSQ_PERF_VS16_THREADS = 12, + HLSQ_PERF_SP_VS_DATA_BYTES = 13, + HLSQ_PERF_SP_FS_DATA_BYTES = 14, + HLSQ_PERF_ACTIVE_CYCLES = 15, + HLSQ_PERF_STALL_CYCLES_SP_STATE = 16, + HLSQ_PERF_STALL_CYCLES_SP_VS = 17, + HLSQ_PERF_STALL_CYCLES_SP_FS = 18, + HLSQ_PERF_STALL_CYCLES_UCHE = 19, + HLSQ_PERF_RBBM_LOAD_CYCLES = 20, + HLSQ_PERF_DI_TO_VS_START_SP0 = 21, + HLSQ_PERF_DI_TO_FS_START_SP0 = 22, + HLSQ_PERF_VS_START_TO_DONE_SP0 = 23, + HLSQ_PERF_FS_START_TO_DONE_SP0 = 24, + HLSQ_PERF_SP_STATE_COPY_CYCLES_VS = 25, + HLSQ_PERF_SP_STATE_COPY_CYCLES_FS = 26, + HLSQ_PERF_UCHE_LATENCY_CYCLES = 27, + HLSQ_PERF_UCHE_LATENCY_COUNT = 28, +}; + +enum a3xx_pc_perfcounter_select { + PC_PCPERF_VISIBILITY_STREAMS = 0, + PC_PCPERF_TOTAL_INSTANCES = 1, + PC_PCPERF_PRIMITIVES_PC_VPC = 2, + PC_PCPERF_PRIMITIVES_KILLED_BY_VS = 3, + PC_PCPERF_PRIMITIVES_VISIBLE_BY_VS = 4, + PC_PCPERF_DRAWCALLS_KILLED_BY_VS = 5, + PC_PCPERF_DRAWCALLS_VISIBLE_BY_VS = 6, + PC_PCPERF_VERTICES_TO_VFD = 7, + PC_PCPERF_REUSED_VERTICES = 8, + PC_PCPERF_CYCLES_STALLED_BY_VFD = 9, + PC_PCPERF_CYCLES_STALLED_BY_TSE = 10, + PC_PCPERF_CYCLES_STALLED_BY_VBIF = 11, + PC_PCPERF_CYCLES_IS_WORKING = 12, +}; + +enum a3xx_rb_perfcounter_select { + RB_RBPERF_ACTIVE_CYCLES_ANY = 0, + RB_RBPERF_ACTIVE_CYCLES_ALL = 1, + RB_RBPERF_STARVE_CYCLES_BY_SP = 2, + RB_RBPERF_STARVE_CYCLES_BY_RAS = 3, + RB_RBPERF_STARVE_CYCLES_BY_MARB = 4, + RB_RBPERF_STALL_CYCLES_BY_MARB = 5, + RB_RBPERF_STALL_CYCLES_BY_HLSQ = 6, + RB_RBPERF_RB_MARB_DATA = 7, + RB_RBPERF_SP_RB_QUAD = 8, + RB_RBPERF_RAS_EARLY_Z_QUADS = 9, + RB_RBPERF_GMEM_CH0_READ = 10, + RB_RBPERF_GMEM_CH1_READ = 11, + RB_RBPERF_GMEM_CH0_WRITE = 12, + RB_RBPERF_GMEM_CH1_WRITE = 13, + RB_RBPERF_CP_CONTEXT_DONE = 14, + RB_RBPERF_CP_CACHE_FLUSH = 15, + RB_RBPERF_CP_ZPASS_DONE = 16, +}; + +enum a3xx_rbbm_perfcounter_select { + RBBM_ALAWYS_ON = 0, + RBBM_VBIF_BUSY = 1, + RBBM_TSE_BUSY = 2, + RBBM_RAS_BUSY = 3, + RBBM_PC_DCALL_BUSY = 4, + RBBM_PC_VSD_BUSY = 5, + RBBM_VFD_BUSY = 6, + RBBM_VPC_BUSY = 7, + RBBM_UCHE_BUSY = 8, + RBBM_VSC_BUSY = 9, + RBBM_HLSQ_BUSY = 10, + RBBM_ANY_RB_BUSY = 11, + RBBM_ANY_TEX_BUSY = 12, + RBBM_ANY_USP_BUSY = 13, + RBBM_ANY_MARB_BUSY = 14, + RBBM_ANY_ARB_BUSY = 15, + RBBM_AHB_STATUS_BUSY = 16, + RBBM_AHB_STATUS_STALLED = 17, + RBBM_AHB_STATUS_TXFR = 18, + RBBM_AHB_STATUS_TXFR_SPLIT = 19, + RBBM_AHB_STATUS_TXFR_ERROR = 20, + RBBM_AHB_STATUS_LONG_STALL = 21, + RBBM_RBBM_STATUS_MASKED = 22, +}; + enum a3xx_sp_perfcounter_select { + SP_LM_LOAD_INSTRUCTIONS = 0, + SP_LM_STORE_INSTRUCTIONS = 1, + SP_LM_ATOMICS = 2, + SP_UCHE_LOAD_INSTRUCTIONS = 3, + SP_UCHE_STORE_INSTRUCTIONS = 4, + SP_UCHE_ATOMICS = 5, + SP_VS_TEX_INSTRUCTIONS = 6, + SP_VS_CFLOW_INSTRUCTIONS = 7, + SP_VS_EFU_INSTRUCTIONS = 8, + SP_VS_FULL_ALU_INSTRUCTIONS = 9, + SP_VS_HALF_ALU_INSTRUCTIONS = 10, + SP_FS_TEX_INSTRUCTIONS = 11, SP_FS_CFLOW_INSTRUCTIONS = 12, + SP_FS_EFU_INSTRUCTIONS = 13, SP_FS_FULL_ALU_INSTRUCTIONS = 14, - SP0_ICL1_MISSES = 26, + SP_FS_HALF_ALU_INSTRUCTIONS = 15, + SP_FS_BARY_INSTRUCTIONS = 16, + SP_VS_INSTRUCTIONS = 17, + SP_FS_INSTRUCTIONS = 18, + SP_ADDR_LOCK_COUNT = 19, + SP_UCHE_READ_TRANS = 20, + SP_UCHE_WRITE_TRANS = 21, + SP_EXPORT_VPC_TRANS = 22, + SP_EXPORT_RB_TRANS = 23, + SP_PIXELS_KILLED = 24, + SP_ICL1_REQUESTS = 25, + SP_ICL1_MISSES = 26, + SP_ICL0_REQUESTS = 27, + SP_ICL0_MISSES = 28, SP_ALU_ACTIVE_CYCLES = 29, + SP_EFU_ACTIVE_CYCLES = 30, + SP_STALL_CYCLES_BY_VPC = 31, + SP_STALL_CYCLES_BY_TP = 32, + SP_STALL_CYCLES_BY_UCHE = 33, + SP_STALL_CYCLES_BY_RB = 34, + SP_ACTIVE_CYCLES_ANY = 35, + SP_ACTIVE_CYCLES_ALL = 36, +}; + +enum a3xx_tp_perfcounter_select { + TPL1_TPPERF_L1_REQUESTS = 0, + TPL1_TPPERF_TP0_L1_REQUESTS = 1, + TPL1_TPPERF_TP0_L1_MISSES = 2, + TPL1_TPPERF_TP1_L1_REQUESTS = 3, + TPL1_TPPERF_TP1_L1_MISSES = 4, + TPL1_TPPERF_TP2_L1_REQUESTS = 5, + TPL1_TPPERF_TP2_L1_MISSES = 6, + TPL1_TPPERF_TP3_L1_REQUESTS = 7, + TPL1_TPPERF_TP3_L1_MISSES = 8, + TPL1_TPPERF_OUTPUT_TEXELS_POINT = 9, + TPL1_TPPERF_OUTPUT_TEXELS_BILINEAR = 10, + TPL1_TPPERF_OUTPUT_TEXELS_MIP = 11, + TPL1_TPPERF_OUTPUT_TEXELS_ANISO = 12, + TPL1_TPPERF_BILINEAR_OPS = 13, + TPL1_TPPERF_QUADSQUADS_OFFSET = 14, + TPL1_TPPERF_QUADQUADS_SHADOW = 15, + TPL1_TPPERF_QUADS_ARRAY = 16, + TPL1_TPPERF_QUADS_PROJECTION = 17, + TPL1_TPPERF_QUADS_GRADIENT = 18, + TPL1_TPPERF_QUADS_1D2D = 19, + TPL1_TPPERF_QUADS_3DCUBE = 20, + TPL1_TPPERF_ZERO_LOD = 21, + TPL1_TPPERF_OUTPUT_TEXELS = 22, + TPL1_TPPERF_ACTIVE_CYCLES_ANY = 23, + TPL1_TPPERF_ACTIVE_CYCLES_ALL = 24, + TPL1_TPPERF_STALL_CYCLES_BY_ARB = 25, + TPL1_TPPERF_LATENCY = 26, + TPL1_TPPERF_LATENCY_TRANS = 27, }; -enum a3xx_rop_code { - ROP_CLEAR = 0, - ROP_NOR = 1, - ROP_AND_INVERTED = 2, - ROP_COPY_INVERTED = 3, - ROP_AND_REVERSE = 4, - ROP_INVERT = 5, - ROP_XOR = 6, - ROP_NAND = 7, - ROP_AND = 8, - ROP_EQUIV = 9, - ROP_NOOP = 10, - ROP_OR_INVERTED = 11, - ROP_COPY = 12, - ROP_OR_REVERSE = 13, - ROP_OR = 14, - ROP_SET = 15, +enum a3xx_vfd_perfcounter_select { + VFD_PERF_UCHE_BYTE_FETCHED = 0, + VFD_PERF_UCHE_TRANS = 1, + VFD_PERF_VPC_BYPASS_COMPONENTS = 2, + VFD_PERF_FETCH_INSTRUCTIONS = 3, + VFD_PERF_DECODE_INSTRUCTIONS = 4, + VFD_PERF_ACTIVE_CYCLES = 5, + VFD_PERF_STALL_CYCLES_UCHE = 6, + VFD_PERF_STALL_CYCLES_HLSQ = 7, + VFD_PERF_STALL_CYCLES_VPC_BYPASS = 8, + VFD_PERF_STALL_CYCLES_VPC_ALLOC = 9, +}; + +enum a3xx_vpc_perfcounter_select { + VPC_PERF_SP_LM_PRIMITIVES = 0, + VPC_PERF_COMPONENTS_FROM_SP = 1, + VPC_PERF_SP_LM_COMPONENTS = 2, + VPC_PERF_ACTIVE_CYCLES = 3, + VPC_PERF_STALL_CYCLES_LM = 4, + VPC_PERF_STALL_CYCLES_RAS = 5, +}; + +enum a3xx_uche_perfcounter_select { + UCHE_UCHEPERF_VBIF_READ_BEATS_TP = 0, + UCHE_UCHEPERF_VBIF_READ_BEATS_VFD = 1, + UCHE_UCHEPERF_VBIF_READ_BEATS_HLSQ = 2, + UCHE_UCHEPERF_VBIF_READ_BEATS_MARB = 3, + UCHE_UCHEPERF_VBIF_READ_BEATS_SP = 4, + UCHE_UCHEPERF_READ_REQUESTS_TP = 8, + UCHE_UCHEPERF_READ_REQUESTS_VFD = 9, + UCHE_UCHEPERF_READ_REQUESTS_HLSQ = 10, + UCHE_UCHEPERF_READ_REQUESTS_MARB = 11, + UCHE_UCHEPERF_READ_REQUESTS_SP = 12, + UCHE_UCHEPERF_WRITE_REQUESTS_MARB = 13, + UCHE_UCHEPERF_WRITE_REQUESTS_SP = 14, + UCHE_UCHEPERF_TAG_CHECK_FAILS = 15, + UCHE_UCHEPERF_EVICTS = 16, + UCHE_UCHEPERF_FLUSHES = 17, + UCHE_UCHEPERF_VBIF_LATENCY_CYCLES = 18, + UCHE_UCHEPERF_VBIF_LATENCY_SAMPLES = 19, + UCHE_UCHEPERF_ACTIVE_CYCLES = 20, }; enum a3xx_rb_blend_opcode { @@ -1429,15 +1684,23 @@ static inline uint32_t A3XX_PC_PRIM_VTX_CNTL_POLYMODE_BACK_PTYPE(enum adreno_pa_ #define REG_A3XX_PC_RESTART_INDEX 0x000021ed #define REG_A3XX_HLSQ_CONTROL_0_REG 0x00002200 -#define A3XX_HLSQ_CONTROL_0_REG_FSTHREADSIZE__MASK 0x00000010 +#define A3XX_HLSQ_CONTROL_0_REG_FSTHREADSIZE__MASK 0x00000030 #define A3XX_HLSQ_CONTROL_0_REG_FSTHREADSIZE__SHIFT 4 static inline uint32_t A3XX_HLSQ_CONTROL_0_REG_FSTHREADSIZE(enum a3xx_threadsize val) { return ((val) << A3XX_HLSQ_CONTROL_0_REG_FSTHREADSIZE__SHIFT) & A3XX_HLSQ_CONTROL_0_REG_FSTHREADSIZE__MASK; } #define A3XX_HLSQ_CONTROL_0_REG_FSSUPERTHREADENABLE 0x00000040 +#define A3XX_HLSQ_CONTROL_0_REG_COMPUTEMODE 0x00000100 #define A3XX_HLSQ_CONTROL_0_REG_SPSHADERRESTART 0x00000200 #define A3XX_HLSQ_CONTROL_0_REG_RESERVED2 0x00000400 +#define A3XX_HLSQ_CONTROL_0_REG_CYCLETIMEOUTLIMITVPC__MASK 0x00fff000 +#define A3XX_HLSQ_CONTROL_0_REG_CYCLETIMEOUTLIMITVPC__SHIFT 12 +static inline uint32_t A3XX_HLSQ_CONTROL_0_REG_CYCLETIMEOUTLIMITVPC(uint32_t val) +{ + return ((val) << A3XX_HLSQ_CONTROL_0_REG_CYCLETIMEOUTLIMITVPC__SHIFT) & A3XX_HLSQ_CONTROL_0_REG_CYCLETIMEOUTLIMITVPC__MASK; +} +#define A3XX_HLSQ_CONTROL_0_REG_FSONLYTEX 0x02000000 #define A3XX_HLSQ_CONTROL_0_REG_CHUNKDISABLE 0x04000000 #define A3XX_HLSQ_CONTROL_0_REG_CONSTMODE__MASK 0x08000000 #define A3XX_HLSQ_CONTROL_0_REG_CONSTMODE__SHIFT 27 @@ -1451,17 +1714,39 @@ static inline uint32_t A3XX_HLSQ_CONTROL_0_REG_CONSTMODE(uint32_t val) #define A3XX_HLSQ_CONTROL_0_REG_SINGLECONTEXT 0x80000000 #define REG_A3XX_HLSQ_CONTROL_1_REG 0x00002201 -#define A3XX_HLSQ_CONTROL_1_REG_VSTHREADSIZE__MASK 0x00000040 +#define A3XX_HLSQ_CONTROL_1_REG_VSTHREADSIZE__MASK 0x000000c0 #define A3XX_HLSQ_CONTROL_1_REG_VSTHREADSIZE__SHIFT 6 static inline uint32_t A3XX_HLSQ_CONTROL_1_REG_VSTHREADSIZE(enum a3xx_threadsize val) { return ((val) << A3XX_HLSQ_CONTROL_1_REG_VSTHREADSIZE__SHIFT) & A3XX_HLSQ_CONTROL_1_REG_VSTHREADSIZE__MASK; } #define A3XX_HLSQ_CONTROL_1_REG_VSSUPERTHREADENABLE 0x00000100 -#define A3XX_HLSQ_CONTROL_1_REG_RESERVED1 0x00000200 -#define A3XX_HLSQ_CONTROL_1_REG_ZWCOORD 0x02000000 +#define A3XX_HLSQ_CONTROL_1_REG_FRAGCOORDXYREGID__MASK 0x00ff0000 +#define A3XX_HLSQ_CONTROL_1_REG_FRAGCOORDXYREGID__SHIFT 16 +static inline uint32_t A3XX_HLSQ_CONTROL_1_REG_FRAGCOORDXYREGID(uint32_t val) +{ + return ((val) << A3XX_HLSQ_CONTROL_1_REG_FRAGCOORDXYREGID__SHIFT) & A3XX_HLSQ_CONTROL_1_REG_FRAGCOORDXYREGID__MASK; +} +#define A3XX_HLSQ_CONTROL_1_REG_FRAGCOORDZWREGID__MASK 0xff000000 +#define A3XX_HLSQ_CONTROL_1_REG_FRAGCOORDZWREGID__SHIFT 24 +static inline uint32_t A3XX_HLSQ_CONTROL_1_REG_FRAGCOORDZWREGID(uint32_t val) +{ + return ((val) << A3XX_HLSQ_CONTROL_1_REG_FRAGCOORDZWREGID__SHIFT) & A3XX_HLSQ_CONTROL_1_REG_FRAGCOORDZWREGID__MASK; +} #define REG_A3XX_HLSQ_CONTROL_2_REG 0x00002202 +#define A3XX_HLSQ_CONTROL_2_REG_FACENESSREGID__MASK 0x000003fc +#define A3XX_HLSQ_CONTROL_2_REG_FACENESSREGID__SHIFT 2 +static inline uint32_t A3XX_HLSQ_CONTROL_2_REG_FACENESSREGID(uint32_t val) +{ + return ((val) << A3XX_HLSQ_CONTROL_2_REG_FACENESSREGID__SHIFT) & A3XX_HLSQ_CONTROL_2_REG_FACENESSREGID__MASK; +} +#define A3XX_HLSQ_CONTROL_2_REG_COVVALUEREGID__MASK 0x03fc0000 +#define A3XX_HLSQ_CONTROL_2_REG_COVVALUEREGID__SHIFT 18 +static inline uint32_t A3XX_HLSQ_CONTROL_2_REG_COVVALUEREGID(uint32_t val) +{ + return ((val) << A3XX_HLSQ_CONTROL_2_REG_COVVALUEREGID__SHIFT) & A3XX_HLSQ_CONTROL_2_REG_COVVALUEREGID__MASK; +} #define A3XX_HLSQ_CONTROL_2_REG_PRIMALLOCTHRESHOLD__MASK 0xfc000000 #define A3XX_HLSQ_CONTROL_2_REG_PRIMALLOCTHRESHOLD__SHIFT 26 static inline uint32_t A3XX_HLSQ_CONTROL_2_REG_PRIMALLOCTHRESHOLD(uint32_t val) @@ -1478,13 +1763,13 @@ static inline uint32_t A3XX_HLSQ_CONTROL_3_REG_REGID(uint32_t val) } #define REG_A3XX_HLSQ_VS_CONTROL_REG 0x00002204 -#define A3XX_HLSQ_VS_CONTROL_REG_CONSTLENGTH__MASK 0x00000fff +#define A3XX_HLSQ_VS_CONTROL_REG_CONSTLENGTH__MASK 0x000003ff #define A3XX_HLSQ_VS_CONTROL_REG_CONSTLENGTH__SHIFT 0 static inline uint32_t A3XX_HLSQ_VS_CONTROL_REG_CONSTLENGTH(uint32_t val) { return ((val) << A3XX_HLSQ_VS_CONTROL_REG_CONSTLENGTH__SHIFT) & A3XX_HLSQ_VS_CONTROL_REG_CONSTLENGTH__MASK; } -#define A3XX_HLSQ_VS_CONTROL_REG_CONSTSTARTOFFSET__MASK 0x00fff000 +#define A3XX_HLSQ_VS_CONTROL_REG_CONSTSTARTOFFSET__MASK 0x001ff000 #define A3XX_HLSQ_VS_CONTROL_REG_CONSTSTARTOFFSET__SHIFT 12 static inline uint32_t A3XX_HLSQ_VS_CONTROL_REG_CONSTSTARTOFFSET(uint32_t val) { @@ -1498,13 +1783,13 @@ static inline uint32_t A3XX_HLSQ_VS_CONTROL_REG_INSTRLENGTH(uint32_t val) } #define REG_A3XX_HLSQ_FS_CONTROL_REG 0x00002205 -#define A3XX_HLSQ_FS_CONTROL_REG_CONSTLENGTH__MASK 0x00000fff +#define A3XX_HLSQ_FS_CONTROL_REG_CONSTLENGTH__MASK 0x000003ff #define A3XX_HLSQ_FS_CONTROL_REG_CONSTLENGTH__SHIFT 0 static inline uint32_t A3XX_HLSQ_FS_CONTROL_REG_CONSTLENGTH(uint32_t val) { return ((val) << A3XX_HLSQ_FS_CONTROL_REG_CONSTLENGTH__SHIFT) & A3XX_HLSQ_FS_CONTROL_REG_CONSTLENGTH__MASK; } -#define A3XX_HLSQ_FS_CONTROL_REG_CONSTSTARTOFFSET__MASK 0x00fff000 +#define A3XX_HLSQ_FS_CONTROL_REG_CONSTSTARTOFFSET__MASK 0x001ff000 #define A3XX_HLSQ_FS_CONTROL_REG_CONSTSTARTOFFSET__SHIFT 12 static inline uint32_t A3XX_HLSQ_FS_CONTROL_REG_CONSTSTARTOFFSET(uint32_t val) { @@ -1518,13 +1803,13 @@ static inline uint32_t A3XX_HLSQ_FS_CONTROL_REG_INSTRLENGTH(uint32_t val) } #define REG_A3XX_HLSQ_CONST_VSPRESV_RANGE_REG 0x00002206 -#define A3XX_HLSQ_CONST_VSPRESV_RANGE_REG_STARTENTRY__MASK 0x0000ffff +#define A3XX_HLSQ_CONST_VSPRESV_RANGE_REG_STARTENTRY__MASK 0x000001ff #define A3XX_HLSQ_CONST_VSPRESV_RANGE_REG_STARTENTRY__SHIFT 0 static inline uint32_t A3XX_HLSQ_CONST_VSPRESV_RANGE_REG_STARTENTRY(uint32_t val) { return ((val) << A3XX_HLSQ_CONST_VSPRESV_RANGE_REG_STARTENTRY__SHIFT) & A3XX_HLSQ_CONST_VSPRESV_RANGE_REG_STARTENTRY__MASK; } -#define A3XX_HLSQ_CONST_VSPRESV_RANGE_REG_ENDENTRY__MASK 0xffff0000 +#define A3XX_HLSQ_CONST_VSPRESV_RANGE_REG_ENDENTRY__MASK 0x01ff0000 #define A3XX_HLSQ_CONST_VSPRESV_RANGE_REG_ENDENTRY__SHIFT 16 static inline uint32_t A3XX_HLSQ_CONST_VSPRESV_RANGE_REG_ENDENTRY(uint32_t val) { @@ -1532,13 +1817,13 @@ static inline uint32_t A3XX_HLSQ_CONST_VSPRESV_RANGE_REG_ENDENTRY(uint32_t val) } #define REG_A3XX_HLSQ_CONST_FSPRESV_RANGE_REG 0x00002207 -#define A3XX_HLSQ_CONST_FSPRESV_RANGE_REG_STARTENTRY__MASK 0x0000ffff +#define A3XX_HLSQ_CONST_FSPRESV_RANGE_REG_STARTENTRY__MASK 0x000001ff #define A3XX_HLSQ_CONST_FSPRESV_RANGE_REG_STARTENTRY__SHIFT 0 static inline uint32_t A3XX_HLSQ_CONST_FSPRESV_RANGE_REG_STARTENTRY(uint32_t val) { return ((val) << A3XX_HLSQ_CONST_FSPRESV_RANGE_REG_STARTENTRY__SHIFT) & A3XX_HLSQ_CONST_FSPRESV_RANGE_REG_STARTENTRY__MASK; } -#define A3XX_HLSQ_CONST_FSPRESV_RANGE_REG_ENDENTRY__MASK 0xffff0000 +#define A3XX_HLSQ_CONST_FSPRESV_RANGE_REG_ENDENTRY__MASK 0x01ff0000 #define A3XX_HLSQ_CONST_FSPRESV_RANGE_REG_ENDENTRY__SHIFT 16 static inline uint32_t A3XX_HLSQ_CONST_FSPRESV_RANGE_REG_ENDENTRY(uint32_t val) { @@ -1620,12 +1905,24 @@ static inline uint32_t A3XX_VFD_CONTROL_0_STRMFETCHINSTRCNT(uint32_t val) } #define REG_A3XX_VFD_CONTROL_1 0x00002241 -#define A3XX_VFD_CONTROL_1_MAXSTORAGE__MASK 0x0000ffff +#define A3XX_VFD_CONTROL_1_MAXSTORAGE__MASK 0x0000000f #define A3XX_VFD_CONTROL_1_MAXSTORAGE__SHIFT 0 static inline uint32_t A3XX_VFD_CONTROL_1_MAXSTORAGE(uint32_t val) { return ((val) << A3XX_VFD_CONTROL_1_MAXSTORAGE__SHIFT) & A3XX_VFD_CONTROL_1_MAXSTORAGE__MASK; } +#define A3XX_VFD_CONTROL_1_MAXTHRESHOLD__MASK 0x000000f0 +#define A3XX_VFD_CONTROL_1_MAXTHRESHOLD__SHIFT 4 +static inline uint32_t A3XX_VFD_CONTROL_1_MAXTHRESHOLD(uint32_t val) +{ + return ((val) << A3XX_VFD_CONTROL_1_MAXTHRESHOLD__SHIFT) & A3XX_VFD_CONTROL_1_MAXTHRESHOLD__MASK; +} +#define A3XX_VFD_CONTROL_1_MINTHRESHOLD__MASK 0x00000f00 +#define A3XX_VFD_CONTROL_1_MINTHRESHOLD__SHIFT 8 +static inline uint32_t A3XX_VFD_CONTROL_1_MINTHRESHOLD(uint32_t val) +{ + return ((val) << A3XX_VFD_CONTROL_1_MINTHRESHOLD__SHIFT) & A3XX_VFD_CONTROL_1_MINTHRESHOLD__MASK; +} #define A3XX_VFD_CONTROL_1_REGID4VTX__MASK 0x00ff0000 #define A3XX_VFD_CONTROL_1_REGID4VTX__SHIFT 16 static inline uint32_t A3XX_VFD_CONTROL_1_REGID4VTX(uint32_t val) @@ -2008,24 +2305,19 @@ static inline uint32_t A3XX_SP_VS_CTRL_REG0_INSTRBUFFERMODE(enum a3xx_instrbuffe return ((val) << A3XX_SP_VS_CTRL_REG0_INSTRBUFFERMODE__SHIFT) & A3XX_SP_VS_CTRL_REG0_INSTRBUFFERMODE__MASK; } #define A3XX_SP_VS_CTRL_REG0_CACHEINVALID 0x00000004 +#define A3XX_SP_VS_CTRL_REG0_ALUSCHMODE 0x00000008 #define A3XX_SP_VS_CTRL_REG0_HALFREGFOOTPRINT__MASK 0x000003f0 #define A3XX_SP_VS_CTRL_REG0_HALFREGFOOTPRINT__SHIFT 4 static inline uint32_t A3XX_SP_VS_CTRL_REG0_HALFREGFOOTPRINT(uint32_t val) { return ((val) << A3XX_SP_VS_CTRL_REG0_HALFREGFOOTPRINT__SHIFT) & A3XX_SP_VS_CTRL_REG0_HALFREGFOOTPRINT__MASK; } -#define A3XX_SP_VS_CTRL_REG0_FULLREGFOOTPRINT__MASK 0x0003fc00 +#define A3XX_SP_VS_CTRL_REG0_FULLREGFOOTPRINT__MASK 0x0000fc00 #define A3XX_SP_VS_CTRL_REG0_FULLREGFOOTPRINT__SHIFT 10 static inline uint32_t A3XX_SP_VS_CTRL_REG0_FULLREGFOOTPRINT(uint32_t val) { return ((val) << A3XX_SP_VS_CTRL_REG0_FULLREGFOOTPRINT__SHIFT) & A3XX_SP_VS_CTRL_REG0_FULLREGFOOTPRINT__MASK; } -#define A3XX_SP_VS_CTRL_REG0_INOUTREGOVERLAP__MASK 0x000c0000 -#define A3XX_SP_VS_CTRL_REG0_INOUTREGOVERLAP__SHIFT 18 -static inline uint32_t A3XX_SP_VS_CTRL_REG0_INOUTREGOVERLAP(uint32_t val) -{ - return ((val) << A3XX_SP_VS_CTRL_REG0_INOUTREGOVERLAP__SHIFT) & A3XX_SP_VS_CTRL_REG0_INOUTREGOVERLAP__MASK; -} #define A3XX_SP_VS_CTRL_REG0_THREADSIZE__MASK 0x00100000 #define A3XX_SP_VS_CTRL_REG0_THREADSIZE__SHIFT 20 static inline uint32_t A3XX_SP_VS_CTRL_REG0_THREADSIZE(enum a3xx_threadsize val) @@ -2033,8 +2325,6 @@ static inline uint32_t A3XX_SP_VS_CTRL_REG0_THREADSIZE(enum a3xx_threadsize val) return ((val) << A3XX_SP_VS_CTRL_REG0_THREADSIZE__SHIFT) & A3XX_SP_VS_CTRL_REG0_THREADSIZE__MASK; } #define A3XX_SP_VS_CTRL_REG0_SUPERTHREADMODE 0x00200000 -#define A3XX_SP_VS_CTRL_REG0_PIXLODENABLE 0x00400000 -#define A3XX_SP_VS_CTRL_REG0_COMPUTEMODE 0x00800000 #define A3XX_SP_VS_CTRL_REG0_LENGTH__MASK 0xff000000 #define A3XX_SP_VS_CTRL_REG0_LENGTH__SHIFT 24 static inline uint32_t A3XX_SP_VS_CTRL_REG0_LENGTH(uint32_t val) @@ -2075,7 +2365,8 @@ static inline uint32_t A3XX_SP_VS_PARAM_REG_PSIZEREGID(uint32_t val) { return ((val) << A3XX_SP_VS_PARAM_REG_PSIZEREGID__SHIFT) & A3XX_SP_VS_PARAM_REG_PSIZEREGID__MASK; } -#define A3XX_SP_VS_PARAM_REG_TOTALVSOUTVAR__MASK 0xfff00000 +#define A3XX_SP_VS_PARAM_REG_POS2DMODE 0x00010000 +#define A3XX_SP_VS_PARAM_REG_TOTALVSOUTVAR__MASK 0x01f00000 #define A3XX_SP_VS_PARAM_REG_TOTALVSOUTVAR__SHIFT 20 static inline uint32_t A3XX_SP_VS_PARAM_REG_TOTALVSOUTVAR(uint32_t val) { @@ -2085,24 +2376,26 @@ static inline uint32_t A3XX_SP_VS_PARAM_REG_TOTALVSOUTVAR(uint32_t val) static inline uint32_t REG_A3XX_SP_VS_OUT(uint32_t i0) { return 0x000022c7 + 0x1*i0; } static inline uint32_t REG_A3XX_SP_VS_OUT_REG(uint32_t i0) { return 0x000022c7 + 0x1*i0; } -#define A3XX_SP_VS_OUT_REG_A_REGID__MASK 0x000001ff +#define A3XX_SP_VS_OUT_REG_A_REGID__MASK 0x000000ff #define A3XX_SP_VS_OUT_REG_A_REGID__SHIFT 0 static inline uint32_t A3XX_SP_VS_OUT_REG_A_REGID(uint32_t val) { return ((val) << A3XX_SP_VS_OUT_REG_A_REGID__SHIFT) & A3XX_SP_VS_OUT_REG_A_REGID__MASK; } +#define A3XX_SP_VS_OUT_REG_A_HALF 0x00000100 #define A3XX_SP_VS_OUT_REG_A_COMPMASK__MASK 0x00001e00 #define A3XX_SP_VS_OUT_REG_A_COMPMASK__SHIFT 9 static inline uint32_t A3XX_SP_VS_OUT_REG_A_COMPMASK(uint32_t val) { return ((val) << A3XX_SP_VS_OUT_REG_A_COMPMASK__SHIFT) & A3XX_SP_VS_OUT_REG_A_COMPMASK__MASK; } -#define A3XX_SP_VS_OUT_REG_B_REGID__MASK 0x01ff0000 +#define A3XX_SP_VS_OUT_REG_B_REGID__MASK 0x00ff0000 #define A3XX_SP_VS_OUT_REG_B_REGID__SHIFT 16 static inline uint32_t A3XX_SP_VS_OUT_REG_B_REGID(uint32_t val) { return ((val) << A3XX_SP_VS_OUT_REG_B_REGID__SHIFT) & A3XX_SP_VS_OUT_REG_B_REGID__MASK; } +#define A3XX_SP_VS_OUT_REG_B_HALF 0x01000000 #define A3XX_SP_VS_OUT_REG_B_COMPMASK__MASK 0x1e000000 #define A3XX_SP_VS_OUT_REG_B_COMPMASK__SHIFT 25 static inline uint32_t A3XX_SP_VS_OUT_REG_B_COMPMASK(uint32_t val) @@ -2113,25 +2406,25 @@ static inline uint32_t A3XX_SP_VS_OUT_REG_B_COMPMASK(uint32_t val) static inline uint32_t REG_A3XX_SP_VS_VPC_DST(uint32_t i0) { return 0x000022d0 + 0x1*i0; } static inline uint32_t REG_A3XX_SP_VS_VPC_DST_REG(uint32_t i0) { return 0x000022d0 + 0x1*i0; } -#define A3XX_SP_VS_VPC_DST_REG_OUTLOC0__MASK 0x000000ff +#define A3XX_SP_VS_VPC_DST_REG_OUTLOC0__MASK 0x0000007f #define A3XX_SP_VS_VPC_DST_REG_OUTLOC0__SHIFT 0 static inline uint32_t A3XX_SP_VS_VPC_DST_REG_OUTLOC0(uint32_t val) { return ((val) << A3XX_SP_VS_VPC_DST_REG_OUTLOC0__SHIFT) & A3XX_SP_VS_VPC_DST_REG_OUTLOC0__MASK; } -#define A3XX_SP_VS_VPC_DST_REG_OUTLOC1__MASK 0x0000ff00 +#define A3XX_SP_VS_VPC_DST_REG_OUTLOC1__MASK 0x00007f00 #define A3XX_SP_VS_VPC_DST_REG_OUTLOC1__SHIFT 8 static inline uint32_t A3XX_SP_VS_VPC_DST_REG_OUTLOC1(uint32_t val) { return ((val) << A3XX_SP_VS_VPC_DST_REG_OUTLOC1__SHIFT) & A3XX_SP_VS_VPC_DST_REG_OUTLOC1__MASK; } -#define A3XX_SP_VS_VPC_DST_REG_OUTLOC2__MASK 0x00ff0000 +#define A3XX_SP_VS_VPC_DST_REG_OUTLOC2__MASK 0x007f0000 #define A3XX_SP_VS_VPC_DST_REG_OUTLOC2__SHIFT 16 static inline uint32_t A3XX_SP_VS_VPC_DST_REG_OUTLOC2(uint32_t val) { return ((val) << A3XX_SP_VS_VPC_DST_REG_OUTLOC2__SHIFT) & A3XX_SP_VS_VPC_DST_REG_OUTLOC2__MASK; } -#define A3XX_SP_VS_VPC_DST_REG_OUTLOC3__MASK 0xff000000 +#define A3XX_SP_VS_VPC_DST_REG_OUTLOC3__MASK 0x7f000000 #define A3XX_SP_VS_VPC_DST_REG_OUTLOC3__SHIFT 24 static inline uint32_t A3XX_SP_VS_VPC_DST_REG_OUTLOC3(uint32_t val) { @@ -2139,6 +2432,12 @@ static inline uint32_t A3XX_SP_VS_VPC_DST_REG_OUTLOC3(uint32_t val) } #define REG_A3XX_SP_VS_OBJ_OFFSET_REG 0x000022d4 +#define A3XX_SP_VS_OBJ_OFFSET_REG_FIRSTEXECINSTROFFSET__MASK 0x0000ffff +#define A3XX_SP_VS_OBJ_OFFSET_REG_FIRSTEXECINSTROFFSET__SHIFT 0 +static inline uint32_t A3XX_SP_VS_OBJ_OFFSET_REG_FIRSTEXECINSTROFFSET(uint32_t val) +{ + return ((val) << A3XX_SP_VS_OBJ_OFFSET_REG_FIRSTEXECINSTROFFSET__SHIFT) & A3XX_SP_VS_OBJ_OFFSET_REG_FIRSTEXECINSTROFFSET__MASK; +} #define A3XX_SP_VS_OBJ_OFFSET_REG_CONSTOBJECTOFFSET__MASK 0x01ff0000 #define A3XX_SP_VS_OBJ_OFFSET_REG_CONSTOBJECTOFFSET__SHIFT 16 static inline uint32_t A3XX_SP_VS_OBJ_OFFSET_REG_CONSTOBJECTOFFSET(uint32_t val) @@ -2155,8 +2454,38 @@ static inline uint32_t A3XX_SP_VS_OBJ_OFFSET_REG_SHADEROBJOFFSET(uint32_t val) #define REG_A3XX_SP_VS_OBJ_START_REG 0x000022d5 #define REG_A3XX_SP_VS_PVT_MEM_PARAM_REG 0x000022d6 +#define A3XX_SP_VS_PVT_MEM_PARAM_REG_MEMSIZEPERITEM__MASK 0x000000ff +#define A3XX_SP_VS_PVT_MEM_PARAM_REG_MEMSIZEPERITEM__SHIFT 0 +static inline uint32_t A3XX_SP_VS_PVT_MEM_PARAM_REG_MEMSIZEPERITEM(uint32_t val) +{ + return ((val) << A3XX_SP_VS_PVT_MEM_PARAM_REG_MEMSIZEPERITEM__SHIFT) & A3XX_SP_VS_PVT_MEM_PARAM_REG_MEMSIZEPERITEM__MASK; +} +#define A3XX_SP_VS_PVT_MEM_PARAM_REG_HWSTACKOFFSET__MASK 0x00ffff00 +#define A3XX_SP_VS_PVT_MEM_PARAM_REG_HWSTACKOFFSET__SHIFT 8 +static inline uint32_t A3XX_SP_VS_PVT_MEM_PARAM_REG_HWSTACKOFFSET(uint32_t val) +{ + return ((val) << A3XX_SP_VS_PVT_MEM_PARAM_REG_HWSTACKOFFSET__SHIFT) & A3XX_SP_VS_PVT_MEM_PARAM_REG_HWSTACKOFFSET__MASK; +} +#define A3XX_SP_VS_PVT_MEM_PARAM_REG_HWSTACKSIZEPERTHREAD__MASK 0xff000000 +#define A3XX_SP_VS_PVT_MEM_PARAM_REG_HWSTACKSIZEPERTHREAD__SHIFT 24 +static inline uint32_t A3XX_SP_VS_PVT_MEM_PARAM_REG_HWSTACKSIZEPERTHREAD(uint32_t val) +{ + return ((val) << A3XX_SP_VS_PVT_MEM_PARAM_REG_HWSTACKSIZEPERTHREAD__SHIFT) & A3XX_SP_VS_PVT_MEM_PARAM_REG_HWSTACKSIZEPERTHREAD__MASK; +} #define REG_A3XX_SP_VS_PVT_MEM_ADDR_REG 0x000022d7 +#define A3XX_SP_VS_PVT_MEM_ADDR_REG_BURSTLEN__MASK 0x0000001f +#define A3XX_SP_VS_PVT_MEM_ADDR_REG_BURSTLEN__SHIFT 0 +static inline uint32_t A3XX_SP_VS_PVT_MEM_ADDR_REG_BURSTLEN(uint32_t val) +{ + return ((val) << A3XX_SP_VS_PVT_MEM_ADDR_REG_BURSTLEN__SHIFT) & A3XX_SP_VS_PVT_MEM_ADDR_REG_BURSTLEN__MASK; +} +#define A3XX_SP_VS_PVT_MEM_ADDR_REG_SHADERSTARTADDRESS__MASK 0xffffffe0 +#define A3XX_SP_VS_PVT_MEM_ADDR_REG_SHADERSTARTADDRESS__SHIFT 5 +static inline uint32_t A3XX_SP_VS_PVT_MEM_ADDR_REG_SHADERSTARTADDRESS(uint32_t val) +{ + return ((val >> 5) << A3XX_SP_VS_PVT_MEM_ADDR_REG_SHADERSTARTADDRESS__SHIFT) & A3XX_SP_VS_PVT_MEM_ADDR_REG_SHADERSTARTADDRESS__MASK; +} #define REG_A3XX_SP_VS_PVT_MEM_SIZE_REG 0x000022d8 @@ -2182,24 +2511,22 @@ static inline uint32_t A3XX_SP_FS_CTRL_REG0_INSTRBUFFERMODE(enum a3xx_instrbuffe return ((val) << A3XX_SP_FS_CTRL_REG0_INSTRBUFFERMODE__SHIFT) & A3XX_SP_FS_CTRL_REG0_INSTRBUFFERMODE__MASK; } #define A3XX_SP_FS_CTRL_REG0_CACHEINVALID 0x00000004 +#define A3XX_SP_FS_CTRL_REG0_ALUSCHMODE 0x00000008 #define A3XX_SP_FS_CTRL_REG0_HALFREGFOOTPRINT__MASK 0x000003f0 #define A3XX_SP_FS_CTRL_REG0_HALFREGFOOTPRINT__SHIFT 4 static inline uint32_t A3XX_SP_FS_CTRL_REG0_HALFREGFOOTPRINT(uint32_t val) { return ((val) << A3XX_SP_FS_CTRL_REG0_HALFREGFOOTPRINT__SHIFT) & A3XX_SP_FS_CTRL_REG0_HALFREGFOOTPRINT__MASK; } -#define A3XX_SP_FS_CTRL_REG0_FULLREGFOOTPRINT__MASK 0x0003fc00 +#define A3XX_SP_FS_CTRL_REG0_FULLREGFOOTPRINT__MASK 0x0000fc00 #define A3XX_SP_FS_CTRL_REG0_FULLREGFOOTPRINT__SHIFT 10 static inline uint32_t A3XX_SP_FS_CTRL_REG0_FULLREGFOOTPRINT(uint32_t val) { return ((val) << A3XX_SP_FS_CTRL_REG0_FULLREGFOOTPRINT__SHIFT) & A3XX_SP_FS_CTRL_REG0_FULLREGFOOTPRINT__MASK; } -#define A3XX_SP_FS_CTRL_REG0_INOUTREGOVERLAP__MASK 0x000c0000 -#define A3XX_SP_FS_CTRL_REG0_INOUTREGOVERLAP__SHIFT 18 -static inline uint32_t A3XX_SP_FS_CTRL_REG0_INOUTREGOVERLAP(uint32_t val) -{ - return ((val) << A3XX_SP_FS_CTRL_REG0_INOUTREGOVERLAP__SHIFT) & A3XX_SP_FS_CTRL_REG0_INOUTREGOVERLAP__MASK; -} +#define A3XX_SP_FS_CTRL_REG0_FSBYPASSENABLE 0x00020000 +#define A3XX_SP_FS_CTRL_REG0_INOUTREGOVERLAP 0x00040000 +#define A3XX_SP_FS_CTRL_REG0_OUTORDERED 0x00080000 #define A3XX_SP_FS_CTRL_REG0_THREADSIZE__MASK 0x00100000 #define A3XX_SP_FS_CTRL_REG0_THREADSIZE__SHIFT 20 static inline uint32_t A3XX_SP_FS_CTRL_REG0_THREADSIZE(enum a3xx_threadsize val) @@ -2235,7 +2562,7 @@ static inline uint32_t A3XX_SP_FS_CTRL_REG1_INITIALOUTSTANDING(uint32_t val) { return ((val) << A3XX_SP_FS_CTRL_REG1_INITIALOUTSTANDING__SHIFT) & A3XX_SP_FS_CTRL_REG1_INITIALOUTSTANDING__MASK; } -#define A3XX_SP_FS_CTRL_REG1_HALFPRECVAROFFSET__MASK 0x3f000000 +#define A3XX_SP_FS_CTRL_REG1_HALFPRECVAROFFSET__MASK 0x7f000000 #define A3XX_SP_FS_CTRL_REG1_HALFPRECVAROFFSET__SHIFT 24 static inline uint32_t A3XX_SP_FS_CTRL_REG1_HALFPRECVAROFFSET(uint32_t val) { @@ -2243,6 +2570,12 @@ static inline uint32_t A3XX_SP_FS_CTRL_REG1_HALFPRECVAROFFSET(uint32_t val) } #define REG_A3XX_SP_FS_OBJ_OFFSET_REG 0x000022e2 +#define A3XX_SP_FS_OBJ_OFFSET_REG_FIRSTEXECINSTROFFSET__MASK 0x0000ffff +#define A3XX_SP_FS_OBJ_OFFSET_REG_FIRSTEXECINSTROFFSET__SHIFT 0 +static inline uint32_t A3XX_SP_FS_OBJ_OFFSET_REG_FIRSTEXECINSTROFFSET(uint32_t val) +{ + return ((val) << A3XX_SP_FS_OBJ_OFFSET_REG_FIRSTEXECINSTROFFSET__SHIFT) & A3XX_SP_FS_OBJ_OFFSET_REG_FIRSTEXECINSTROFFSET__MASK; +} #define A3XX_SP_FS_OBJ_OFFSET_REG_CONSTOBJECTOFFSET__MASK 0x01ff0000 #define A3XX_SP_FS_OBJ_OFFSET_REG_CONSTOBJECTOFFSET__SHIFT 16 static inline uint32_t A3XX_SP_FS_OBJ_OFFSET_REG_CONSTOBJECTOFFSET(uint32_t val) @@ -2259,8 +2592,38 @@ static inline uint32_t A3XX_SP_FS_OBJ_OFFSET_REG_SHADEROBJOFFSET(uint32_t val) #define REG_A3XX_SP_FS_OBJ_START_REG 0x000022e3 #define REG_A3XX_SP_FS_PVT_MEM_PARAM_REG 0x000022e4 +#define A3XX_SP_FS_PVT_MEM_PARAM_REG_MEMSIZEPERITEM__MASK 0x000000ff +#define A3XX_SP_FS_PVT_MEM_PARAM_REG_MEMSIZEPERITEM__SHIFT 0 +static inline uint32_t A3XX_SP_FS_PVT_MEM_PARAM_REG_MEMSIZEPERITEM(uint32_t val) +{ + return ((val) << A3XX_SP_FS_PVT_MEM_PARAM_REG_MEMSIZEPERITEM__SHIFT) & A3XX_SP_FS_PVT_MEM_PARAM_REG_MEMSIZEPERITEM__MASK; +} +#define A3XX_SP_FS_PVT_MEM_PARAM_REG_HWSTACKOFFSET__MASK 0x00ffff00 +#define A3XX_SP_FS_PVT_MEM_PARAM_REG_HWSTACKOFFSET__SHIFT 8 +static inline uint32_t A3XX_SP_FS_PVT_MEM_PARAM_REG_HWSTACKOFFSET(uint32_t val) +{ + return ((val) << A3XX_SP_FS_PVT_MEM_PARAM_REG_HWSTACKOFFSET__SHIFT) & A3XX_SP_FS_PVT_MEM_PARAM_REG_HWSTACKOFFSET__MASK; +} +#define A3XX_SP_FS_PVT_MEM_PARAM_REG_HWSTACKSIZEPERTHREAD__MASK 0xff000000 +#define A3XX_SP_FS_PVT_MEM_PARAM_REG_HWSTACKSIZEPERTHREAD__SHIFT 24 +static inline uint32_t A3XX_SP_FS_PVT_MEM_PARAM_REG_HWSTACKSIZEPERTHREAD(uint32_t val) +{ + return ((val) << A3XX_SP_FS_PVT_MEM_PARAM_REG_HWSTACKSIZEPERTHREAD__SHIFT) & A3XX_SP_FS_PVT_MEM_PARAM_REG_HWSTACKSIZEPERTHREAD__MASK; +} #define REG_A3XX_SP_FS_PVT_MEM_ADDR_REG 0x000022e5 +#define A3XX_SP_FS_PVT_MEM_ADDR_REG_BURSTLEN__MASK 0x0000001f +#define A3XX_SP_FS_PVT_MEM_ADDR_REG_BURSTLEN__SHIFT 0 +static inline uint32_t A3XX_SP_FS_PVT_MEM_ADDR_REG_BURSTLEN(uint32_t val) +{ + return ((val) << A3XX_SP_FS_PVT_MEM_ADDR_REG_BURSTLEN__SHIFT) & A3XX_SP_FS_PVT_MEM_ADDR_REG_BURSTLEN__MASK; +} +#define A3XX_SP_FS_PVT_MEM_ADDR_REG_SHADERSTARTADDRESS__MASK 0xffffffe0 +#define A3XX_SP_FS_PVT_MEM_ADDR_REG_SHADERSTARTADDRESS__SHIFT 5 +static inline uint32_t A3XX_SP_FS_PVT_MEM_ADDR_REG_SHADERSTARTADDRESS(uint32_t val) +{ + return ((val >> 5) << A3XX_SP_FS_PVT_MEM_ADDR_REG_SHADERSTARTADDRESS__SHIFT) & A3XX_SP_FS_PVT_MEM_ADDR_REG_SHADERSTARTADDRESS__MASK; +} #define REG_A3XX_SP_FS_PVT_MEM_SIZE_REG 0x000022e6 diff --git a/drivers/gpu/drm/msm/adreno/a4xx.xml.h b/drivers/gpu/drm/msm/adreno/a4xx.xml.h index 99de8271dba8..3220b91f559a 100644 --- a/drivers/gpu/drm/msm/adreno/a4xx.xml.h +++ b/drivers/gpu/drm/msm/adreno/a4xx.xml.h @@ -9,16 +9,17 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 398 bytes, from 2015-09-24 17:25:31) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) - /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10755 bytes, from 2015-09-14 20:46:55) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14968 bytes, from 2015-05-20 20:12:27) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 67771 bytes, from 2015-09-14 20:46:55) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 63970 bytes, from 2015-09-14 20:50:12) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 11518 bytes, from 2016-02-10 21:03:25) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 16166 bytes, from 2016-02-11 21:20:31) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 83967 bytes, from 2016-02-10 17:07:21) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 109916 bytes, from 2016-02-20 18:44:48) - /home/robclark/src/freedreno/envytools/rnndb/adreno/ocmem.xml ( 1773 bytes, from 2015-09-24 17:30:00) -Copyright (C) 2013-2015 by the following authors: +Copyright (C) 2013-2016 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) +- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -47,11 +48,13 @@ enum a4xx_color_fmt { RB4_R8_UNORM = 2, RB4_R4G4B4A4_UNORM = 8, RB4_R5G5B5A1_UNORM = 10, - RB4_R5G6R5_UNORM = 14, + RB4_R5G6B5_UNORM = 14, RB4_R8G8_UNORM = 15, RB4_R8G8_SNORM = 16, RB4_R8G8_UINT = 17, RB4_R8G8_SINT = 18, + RB4_R16_UNORM = 19, + RB4_R16_SNORM = 20, RB4_R16_FLOAT = 21, RB4_R16_UINT = 22, RB4_R16_SINT = 23, @@ -63,12 +66,16 @@ enum a4xx_color_fmt { RB4_R10G10B10A2_UNORM = 31, RB4_R10G10B10A2_UINT = 34, RB4_R11G11B10_FLOAT = 39, + RB4_R16G16_UNORM = 40, + RB4_R16G16_SNORM = 41, RB4_R16G16_FLOAT = 42, RB4_R16G16_UINT = 43, RB4_R16G16_SINT = 44, RB4_R32_FLOAT = 45, RB4_R32_UINT = 46, RB4_R32_SINT = 47, + RB4_R16G16B16A16_UNORM = 52, + RB4_R16G16B16A16_SNORM = 53, RB4_R16G16B16A16_FLOAT = 54, RB4_R16G16B16A16_UINT = 55, RB4_R16G16B16A16_SINT = 56, @@ -106,6 +113,7 @@ enum a4xx_vtx_fmt { VFMT4_32_32_FIXED = 10, VFMT4_32_32_32_FIXED = 11, VFMT4_32_32_32_32_FIXED = 12, + VFMT4_11_11_10_FLOAT = 13, VFMT4_16_SINT = 16, VFMT4_16_16_SINT = 17, VFMT4_16_16_16_SINT = 18, @@ -146,52 +154,76 @@ enum a4xx_vtx_fmt { VFMT4_8_8_SNORM = 53, VFMT4_8_8_8_SNORM = 54, VFMT4_8_8_8_8_SNORM = 55, - VFMT4_10_10_10_2_UINT = 60, - VFMT4_10_10_10_2_UNORM = 61, - VFMT4_10_10_10_2_SINT = 62, - VFMT4_10_10_10_2_SNORM = 63, + VFMT4_10_10_10_2_UINT = 56, + VFMT4_10_10_10_2_UNORM = 57, + VFMT4_10_10_10_2_SINT = 58, + VFMT4_10_10_10_2_SNORM = 59, + VFMT4_2_10_10_10_UINT = 60, + VFMT4_2_10_10_10_UNORM = 61, + VFMT4_2_10_10_10_SINT = 62, + VFMT4_2_10_10_10_SNORM = 63, }; enum a4xx_tex_fmt { - TFMT4_5_6_5_UNORM = 11, - TFMT4_5_5_5_1_UNORM = 10, - TFMT4_4_4_4_4_UNORM = 8, - TFMT4_X8Z24_UNORM = 71, - TFMT4_10_10_10_2_UNORM = 33, TFMT4_A8_UNORM = 3, - TFMT4_L8_A8_UNORM = 13, TFMT4_8_UNORM = 4, - TFMT4_8_8_UNORM = 14, - TFMT4_8_8_8_8_UNORM = 28, TFMT4_8_SNORM = 5, - TFMT4_8_8_SNORM = 15, - TFMT4_8_8_8_8_SNORM = 29, TFMT4_8_UINT = 6, - TFMT4_8_8_UINT = 16, - TFMT4_8_8_8_8_UINT = 30, TFMT4_8_SINT = 7, + TFMT4_4_4_4_4_UNORM = 8, + TFMT4_5_5_5_1_UNORM = 9, + TFMT4_5_6_5_UNORM = 11, + TFMT4_L8_A8_UNORM = 13, + TFMT4_8_8_UNORM = 14, + TFMT4_8_8_SNORM = 15, + TFMT4_8_8_UINT = 16, TFMT4_8_8_SINT = 17, - TFMT4_8_8_8_8_SINT = 31, + TFMT4_16_UNORM = 18, + TFMT4_16_SNORM = 19, + TFMT4_16_FLOAT = 20, TFMT4_16_UINT = 21, - TFMT4_16_16_UINT = 41, - TFMT4_16_16_16_16_UINT = 54, TFMT4_16_SINT = 22, + TFMT4_8_8_8_8_UNORM = 28, + TFMT4_8_8_8_8_SNORM = 29, + TFMT4_8_8_8_8_UINT = 30, + TFMT4_8_8_8_8_SINT = 31, + TFMT4_9_9_9_E5_FLOAT = 32, + TFMT4_10_10_10_2_UNORM = 33, + TFMT4_10_10_10_2_UINT = 34, + TFMT4_11_11_10_FLOAT = 37, + TFMT4_16_16_UNORM = 38, + TFMT4_16_16_SNORM = 39, + TFMT4_16_16_FLOAT = 40, + TFMT4_16_16_UINT = 41, TFMT4_16_16_SINT = 42, - TFMT4_16_16_16_16_SINT = 55, + TFMT4_32_FLOAT = 43, TFMT4_32_UINT = 44, - TFMT4_32_32_UINT = 57, - TFMT4_32_32_32_32_UINT = 64, TFMT4_32_SINT = 45, - TFMT4_32_32_SINT = 58, - TFMT4_32_32_32_32_SINT = 65, - TFMT4_16_FLOAT = 20, - TFMT4_16_16_FLOAT = 40, + TFMT4_16_16_16_16_UNORM = 51, + TFMT4_16_16_16_16_SNORM = 52, TFMT4_16_16_16_16_FLOAT = 53, - TFMT4_32_FLOAT = 43, + TFMT4_16_16_16_16_UINT = 54, + TFMT4_16_16_16_16_SINT = 55, TFMT4_32_32_FLOAT = 56, + TFMT4_32_32_UINT = 57, + TFMT4_32_32_SINT = 58, + TFMT4_32_32_32_FLOAT = 59, + TFMT4_32_32_32_UINT = 60, + TFMT4_32_32_32_SINT = 61, TFMT4_32_32_32_32_FLOAT = 63, - TFMT4_9_9_9_E5_FLOAT = 32, - TFMT4_11_11_10_FLOAT = 37, + TFMT4_32_32_32_32_UINT = 64, + TFMT4_32_32_32_32_SINT = 65, + TFMT4_X8Z24_UNORM = 71, + TFMT4_DXT1 = 86, + TFMT4_DXT3 = 87, + TFMT4_DXT5 = 88, + TFMT4_RGTC1_UNORM = 90, + TFMT4_RGTC1_SNORM = 91, + TFMT4_RGTC2_UNORM = 94, + TFMT4_RGTC2_SNORM = 95, + TFMT4_BPTC_UFLOAT = 97, + TFMT4_BPTC_FLOAT = 98, + TFMT4_BPTC = 99, TFMT4_ATC_RGB = 100, TFMT4_ATC_RGBA_EXPLICIT = 101, TFMT4_ATC_RGBA_INTERPOLATED = 102, @@ -240,6 +272,545 @@ enum a4xx_tess_spacing { EVEN_SPACING = 3, }; +enum a4xx_ccu_perfcounter_select { + CCU_BUSY_CYCLES = 0, + CCU_RB_DEPTH_RETURN_STALL = 2, + CCU_RB_COLOR_RETURN_STALL = 3, + CCU_DEPTH_BLOCKS = 6, + CCU_COLOR_BLOCKS = 7, + CCU_DEPTH_BLOCK_HIT = 8, + CCU_COLOR_BLOCK_HIT = 9, + CCU_DEPTH_FLAG1_COUNT = 10, + CCU_DEPTH_FLAG2_COUNT = 11, + CCU_DEPTH_FLAG3_COUNT = 12, + CCU_DEPTH_FLAG4_COUNT = 13, + CCU_COLOR_FLAG1_COUNT = 14, + CCU_COLOR_FLAG2_COUNT = 15, + CCU_COLOR_FLAG3_COUNT = 16, + CCU_COLOR_FLAG4_COUNT = 17, + CCU_PARTIAL_BLOCK_READ = 18, +}; + +enum a4xx_cp_perfcounter_select { + CP_ALWAYS_COUNT = 0, + CP_BUSY = 1, + CP_PFP_IDLE = 2, + CP_PFP_BUSY_WORKING = 3, + CP_PFP_STALL_CYCLES_ANY = 4, + CP_PFP_STARVE_CYCLES_ANY = 5, + CP_PFP_STARVED_PER_LOAD_ADDR = 6, + CP_PFP_STALLED_PER_STORE_ADDR = 7, + CP_PFP_PC_PROFILE = 8, + CP_PFP_MATCH_PM4_PKT_PROFILE = 9, + CP_PFP_COND_INDIRECT_DISCARDED = 10, + CP_LONG_RESUMPTIONS = 11, + CP_RESUME_CYCLES = 12, + CP_RESUME_TO_BOUNDARY_CYCLES = 13, + CP_LONG_PREEMPTIONS = 14, + CP_PREEMPT_CYCLES = 15, + CP_PREEMPT_TO_BOUNDARY_CYCLES = 16, + CP_ME_FIFO_EMPTY_PFP_IDLE = 17, + CP_ME_FIFO_EMPTY_PFP_BUSY = 18, + CP_ME_FIFO_NOT_EMPTY_NOT_FULL = 19, + CP_ME_FIFO_FULL_ME_BUSY = 20, + CP_ME_FIFO_FULL_ME_NON_WORKING = 21, + CP_ME_WAITING_FOR_PACKETS = 22, + CP_ME_BUSY_WORKING = 23, + CP_ME_STARVE_CYCLES_ANY = 24, + CP_ME_STARVE_CYCLES_PER_PROFILE = 25, + CP_ME_STALL_CYCLES_PER_PROFILE = 26, + CP_ME_PC_PROFILE = 27, + CP_RCIU_FIFO_EMPTY = 28, + CP_RCIU_FIFO_NOT_EMPTY_NOT_FULL = 29, + CP_RCIU_FIFO_FULL = 30, + CP_RCIU_FIFO_FULL_NO_CONTEXT = 31, + CP_RCIU_FIFO_FULL_AHB_MASTER = 32, + CP_RCIU_FIFO_FULL_OTHER = 33, + CP_AHB_IDLE = 34, + CP_AHB_STALL_ON_GRANT_NO_SPLIT = 35, + CP_AHB_STALL_ON_GRANT_SPLIT = 36, + CP_AHB_STALL_ON_GRANT_SPLIT_PROFILE = 37, + CP_AHB_BUSY_WORKING = 38, + CP_AHB_BUSY_STALL_ON_HRDY = 39, + CP_AHB_BUSY_STALL_ON_HRDY_PROFILE = 40, +}; + +enum a4xx_gras_ras_perfcounter_select { + RAS_SUPER_TILES = 0, + RAS_8X8_TILES = 1, + RAS_4X4_TILES = 2, + RAS_BUSY_CYCLES = 3, + RAS_STALL_CYCLES_BY_RB = 4, + RAS_STALL_CYCLES_BY_VSC = 5, + RAS_STARVE_CYCLES_BY_TSE = 6, + RAS_SUPERTILE_CYCLES = 7, + RAS_TILE_CYCLES = 8, + RAS_FULLY_COVERED_SUPER_TILES = 9, + RAS_FULLY_COVERED_8X8_TILES = 10, + RAS_4X4_PRIM = 11, + RAS_8X4_4X8_PRIM = 12, + RAS_8X8_PRIM = 13, +}; + +enum a4xx_gras_tse_perfcounter_select { + TSE_INPUT_PRIM = 0, + TSE_INPUT_NULL_PRIM = 1, + TSE_TRIVAL_REJ_PRIM = 2, + TSE_CLIPPED_PRIM = 3, + TSE_NEW_PRIM = 4, + TSE_ZERO_AREA_PRIM = 5, + TSE_FACENESS_CULLED_PRIM = 6, + TSE_ZERO_PIXEL_PRIM = 7, + TSE_OUTPUT_NULL_PRIM = 8, + TSE_OUTPUT_VISIBLE_PRIM = 9, + TSE_PRE_CLIP_PRIM = 10, + TSE_POST_CLIP_PRIM = 11, + TSE_BUSY_CYCLES = 12, + TSE_PC_STARVE = 13, + TSE_RAS_STALL = 14, + TSE_STALL_BARYPLANE_FIFO_FULL = 15, + TSE_STALL_ZPLANE_FIFO_FULL = 16, +}; + +enum a4xx_hlsq_perfcounter_select { + HLSQ_SP_VS_STAGE_CONSTANT = 0, + HLSQ_SP_VS_STAGE_INSTRUCTIONS = 1, + HLSQ_SP_FS_STAGE_CONSTANT = 2, + HLSQ_SP_FS_STAGE_INSTRUCTIONS = 3, + HLSQ_TP_STATE = 4, + HLSQ_QUADS = 5, + HLSQ_PIXELS = 6, + HLSQ_VERTICES = 7, + HLSQ_SP_VS_STAGE_DATA_BYTES = 13, + HLSQ_SP_FS_STAGE_DATA_BYTES = 14, + HLSQ_BUSY_CYCLES = 15, + HLSQ_STALL_CYCLES_SP_STATE = 16, + HLSQ_STALL_CYCLES_SP_VS_STAGE = 17, + HLSQ_STALL_CYCLES_SP_FS_STAGE = 18, + HLSQ_STALL_CYCLES_UCHE = 19, + HLSQ_RBBM_LOAD_CYCLES = 20, + HLSQ_DI_TO_VS_START_SP = 21, + HLSQ_DI_TO_FS_START_SP = 22, + HLSQ_VS_STAGE_START_TO_DONE_SP = 23, + HLSQ_FS_STAGE_START_TO_DONE_SP = 24, + HLSQ_SP_STATE_COPY_CYCLES_VS_STAGE = 25, + HLSQ_SP_STATE_COPY_CYCLES_FS_STAGE = 26, + HLSQ_UCHE_LATENCY_CYCLES = 27, + HLSQ_UCHE_LATENCY_COUNT = 28, + HLSQ_STARVE_CYCLES_VFD = 29, +}; + +enum a4xx_pc_perfcounter_select { + PC_VIS_STREAMS_LOADED = 0, + PC_VPC_PRIMITIVES = 2, + PC_DEAD_PRIM = 3, + PC_LIVE_PRIM = 4, + PC_DEAD_DRAWCALLS = 5, + PC_LIVE_DRAWCALLS = 6, + PC_VERTEX_MISSES = 7, + PC_STALL_CYCLES_VFD = 9, + PC_STALL_CYCLES_TSE = 10, + PC_STALL_CYCLES_UCHE = 11, + PC_WORKING_CYCLES = 12, + PC_IA_VERTICES = 13, + PC_GS_PRIMITIVES = 14, + PC_HS_INVOCATIONS = 15, + PC_DS_INVOCATIONS = 16, + PC_DS_PRIMITIVES = 17, + PC_STARVE_CYCLES_FOR_INDEX = 20, + PC_STARVE_CYCLES_FOR_TESS_FACTOR = 21, + PC_STARVE_CYCLES_FOR_VIZ_STREAM = 22, + PC_STALL_CYCLES_TESS = 23, + PC_STARVE_CYCLES_FOR_POSITION = 24, + PC_MODE0_DRAWCALL = 25, + PC_MODE1_DRAWCALL = 26, + PC_MODE2_DRAWCALL = 27, + PC_MODE3_DRAWCALL = 28, + PC_MODE4_DRAWCALL = 29, + PC_PREDICATED_DEAD_DRAWCALL = 30, + PC_STALL_CYCLES_BY_TSE_ONLY = 31, + PC_STALL_CYCLES_BY_VPC_ONLY = 32, + PC_VPC_POS_DATA_TRANSACTION = 33, + PC_BUSY_CYCLES = 34, + PC_STARVE_CYCLES_DI = 35, + PC_STALL_CYCLES_VPC = 36, + TESS_WORKING_CYCLES = 37, + TESS_NUM_CYCLES_SETUP_WORKING = 38, + TESS_NUM_CYCLES_PTGEN_WORKING = 39, + TESS_NUM_CYCLES_CONNGEN_WORKING = 40, + TESS_BUSY_CYCLES = 41, + TESS_STARVE_CYCLES_PC = 42, + TESS_STALL_CYCLES_PC = 43, +}; + +enum a4xx_pwr_perfcounter_select { + PWR_CORE_CLOCK_CYCLES = 0, + PWR_BUSY_CLOCK_CYCLES = 1, +}; + +enum a4xx_rb_perfcounter_select { + RB_BUSY_CYCLES = 0, + RB_BUSY_CYCLES_BINNING = 1, + RB_BUSY_CYCLES_RENDERING = 2, + RB_BUSY_CYCLES_RESOLVE = 3, + RB_STARVE_CYCLES_BY_SP = 4, + RB_STARVE_CYCLES_BY_RAS = 5, + RB_STARVE_CYCLES_BY_MARB = 6, + RB_STALL_CYCLES_BY_MARB = 7, + RB_STALL_CYCLES_BY_HLSQ = 8, + RB_RB_RB_MARB_DATA = 9, + RB_SP_RB_QUAD = 10, + RB_RAS_RB_Z_QUADS = 11, + RB_GMEM_CH0_READ = 12, + RB_GMEM_CH1_READ = 13, + RB_GMEM_CH0_WRITE = 14, + RB_GMEM_CH1_WRITE = 15, + RB_CP_CONTEXT_DONE = 16, + RB_CP_CACHE_FLUSH = 17, + RB_CP_ZPASS_DONE = 18, + RB_STALL_FIFO0_FULL = 19, + RB_STALL_FIFO1_FULL = 20, + RB_STALL_FIFO2_FULL = 21, + RB_STALL_FIFO3_FULL = 22, + RB_RB_HLSQ_TRANSACTIONS = 23, + RB_Z_READ = 24, + RB_Z_WRITE = 25, + RB_C_READ = 26, + RB_C_WRITE = 27, + RB_C_READ_LATENCY = 28, + RB_Z_READ_LATENCY = 29, + RB_STALL_BY_UCHE = 30, + RB_MARB_UCHE_TRANSACTIONS = 31, + RB_CACHE_STALL_MISS = 32, + RB_CACHE_STALL_FIFO_FULL = 33, + RB_8BIT_BLENDER_UNITS_ACTIVE = 34, + RB_16BIT_BLENDER_UNITS_ACTIVE = 35, + RB_SAMPLER_UNITS_ACTIVE = 36, + RB_TOTAL_PASS = 38, + RB_Z_PASS = 39, + RB_Z_FAIL = 40, + RB_S_FAIL = 41, + RB_POWER0 = 42, + RB_POWER1 = 43, + RB_POWER2 = 44, + RB_POWER3 = 45, + RB_POWER4 = 46, + RB_POWER5 = 47, + RB_POWER6 = 48, + RB_POWER7 = 49, +}; + +enum a4xx_rbbm_perfcounter_select { + RBBM_ALWAYS_ON = 0, + RBBM_VBIF_BUSY = 1, + RBBM_TSE_BUSY = 2, + RBBM_RAS_BUSY = 3, + RBBM_PC_DCALL_BUSY = 4, + RBBM_PC_VSD_BUSY = 5, + RBBM_VFD_BUSY = 6, + RBBM_VPC_BUSY = 7, + RBBM_UCHE_BUSY = 8, + RBBM_VSC_BUSY = 9, + RBBM_HLSQ_BUSY = 10, + RBBM_ANY_RB_BUSY = 11, + RBBM_ANY_TPL1_BUSY = 12, + RBBM_ANY_SP_BUSY = 13, + RBBM_ANY_MARB_BUSY = 14, + RBBM_ANY_ARB_BUSY = 15, + RBBM_AHB_STATUS_BUSY = 16, + RBBM_AHB_STATUS_STALLED = 17, + RBBM_AHB_STATUS_TXFR = 18, + RBBM_AHB_STATUS_TXFR_SPLIT = 19, + RBBM_AHB_STATUS_TXFR_ERROR = 20, + RBBM_AHB_STATUS_LONG_STALL = 21, + RBBM_STATUS_MASKED = 22, + RBBM_CP_BUSY_GFX_CORE_IDLE = 23, + RBBM_TESS_BUSY = 24, + RBBM_COM_BUSY = 25, + RBBM_DCOM_BUSY = 32, + RBBM_ANY_CCU_BUSY = 33, + RBBM_DPM_BUSY = 34, +}; + +enum a4xx_sp_perfcounter_select { + SP_LM_LOAD_INSTRUCTIONS = 0, + SP_LM_STORE_INSTRUCTIONS = 1, + SP_LM_ATOMICS = 2, + SP_GM_LOAD_INSTRUCTIONS = 3, + SP_GM_STORE_INSTRUCTIONS = 4, + SP_GM_ATOMICS = 5, + SP_VS_STAGE_TEX_INSTRUCTIONS = 6, + SP_VS_STAGE_CFLOW_INSTRUCTIONS = 7, + SP_VS_STAGE_EFU_INSTRUCTIONS = 8, + SP_VS_STAGE_FULL_ALU_INSTRUCTIONS = 9, + SP_VS_STAGE_HALF_ALU_INSTRUCTIONS = 10, + SP_FS_STAGE_TEX_INSTRUCTIONS = 11, + SP_FS_STAGE_CFLOW_INSTRUCTIONS = 12, + SP_FS_STAGE_EFU_INSTRUCTIONS = 13, + SP_FS_STAGE_FULL_ALU_INSTRUCTIONS = 14, + SP_FS_STAGE_HALF_ALU_INSTRUCTIONS = 15, + SP_VS_INSTRUCTIONS = 17, + SP_FS_INSTRUCTIONS = 18, + SP_ADDR_LOCK_COUNT = 19, + SP_UCHE_READ_TRANS = 20, + SP_UCHE_WRITE_TRANS = 21, + SP_EXPORT_VPC_TRANS = 22, + SP_EXPORT_RB_TRANS = 23, + SP_PIXELS_KILLED = 24, + SP_ICL1_REQUESTS = 25, + SP_ICL1_MISSES = 26, + SP_ICL0_REQUESTS = 27, + SP_ICL0_MISSES = 28, + SP_ALU_WORKING_CYCLES = 29, + SP_EFU_WORKING_CYCLES = 30, + SP_STALL_CYCLES_BY_VPC = 31, + SP_STALL_CYCLES_BY_TP = 32, + SP_STALL_CYCLES_BY_UCHE = 33, + SP_STALL_CYCLES_BY_RB = 34, + SP_BUSY_CYCLES = 35, + SP_HS_INSTRUCTIONS = 36, + SP_DS_INSTRUCTIONS = 37, + SP_GS_INSTRUCTIONS = 38, + SP_CS_INSTRUCTIONS = 39, + SP_SCHEDULER_NON_WORKING = 40, + SP_WAVE_CONTEXTS = 41, + SP_WAVE_CONTEXT_CYCLES = 42, + SP_POWER0 = 43, + SP_POWER1 = 44, + SP_POWER2 = 45, + SP_POWER3 = 46, + SP_POWER4 = 47, + SP_POWER5 = 48, + SP_POWER6 = 49, + SP_POWER7 = 50, + SP_POWER8 = 51, + SP_POWER9 = 52, + SP_POWER10 = 53, + SP_POWER11 = 54, + SP_POWER12 = 55, + SP_POWER13 = 56, + SP_POWER14 = 57, + SP_POWER15 = 58, +}; + +enum a4xx_tp_perfcounter_select { + TP_L1_REQUESTS = 0, + TP_L1_MISSES = 1, + TP_QUADS_OFFSET = 8, + TP_QUAD_SHADOW = 9, + TP_QUADS_ARRAY = 10, + TP_QUADS_GRADIENT = 11, + TP_QUADS_1D2D = 12, + TP_QUADS_3DCUBE = 13, + TP_BUSY_CYCLES = 16, + TP_STALL_CYCLES_BY_ARB = 17, + TP_STATE_CACHE_REQUESTS = 20, + TP_STATE_CACHE_MISSES = 21, + TP_POWER0 = 22, + TP_POWER1 = 23, + TP_POWER2 = 24, + TP_POWER3 = 25, + TP_POWER4 = 26, + TP_POWER5 = 27, + TP_POWER6 = 28, + TP_POWER7 = 29, +}; + +enum a4xx_uche_perfcounter_select { + UCHE_VBIF_READ_BEATS_TP = 0, + UCHE_VBIF_READ_BEATS_VFD = 1, + UCHE_VBIF_READ_BEATS_HLSQ = 2, + UCHE_VBIF_READ_BEATS_MARB = 3, + UCHE_VBIF_READ_BEATS_SP = 4, + UCHE_READ_REQUESTS_TP = 5, + UCHE_READ_REQUESTS_VFD = 6, + UCHE_READ_REQUESTS_HLSQ = 7, + UCHE_READ_REQUESTS_MARB = 8, + UCHE_READ_REQUESTS_SP = 9, + UCHE_WRITE_REQUESTS_MARB = 10, + UCHE_WRITE_REQUESTS_SP = 11, + UCHE_TAG_CHECK_FAILS = 12, + UCHE_EVICTS = 13, + UCHE_FLUSHES = 14, + UCHE_VBIF_LATENCY_CYCLES = 15, + UCHE_VBIF_LATENCY_SAMPLES = 16, + UCHE_BUSY_CYCLES = 17, + UCHE_VBIF_READ_BEATS_PC = 18, + UCHE_READ_REQUESTS_PC = 19, + UCHE_WRITE_REQUESTS_VPC = 20, + UCHE_STALL_BY_VBIF = 21, + UCHE_WRITE_REQUESTS_VSC = 22, + UCHE_POWER0 = 23, + UCHE_POWER1 = 24, + UCHE_POWER2 = 25, + UCHE_POWER3 = 26, + UCHE_POWER4 = 27, + UCHE_POWER5 = 28, + UCHE_POWER6 = 29, + UCHE_POWER7 = 30, +}; + +enum a4xx_vbif_perfcounter_select { + AXI_READ_REQUESTS_ID_0 = 0, + AXI_READ_REQUESTS_ID_1 = 1, + AXI_READ_REQUESTS_ID_2 = 2, + AXI_READ_REQUESTS_ID_3 = 3, + AXI_READ_REQUESTS_ID_4 = 4, + AXI_READ_REQUESTS_ID_5 = 5, + AXI_READ_REQUESTS_ID_6 = 6, + AXI_READ_REQUESTS_ID_7 = 7, + AXI_READ_REQUESTS_ID_8 = 8, + AXI_READ_REQUESTS_ID_9 = 9, + AXI_READ_REQUESTS_ID_10 = 10, + AXI_READ_REQUESTS_ID_11 = 11, + AXI_READ_REQUESTS_ID_12 = 12, + AXI_READ_REQUESTS_ID_13 = 13, + AXI_READ_REQUESTS_ID_14 = 14, + AXI_READ_REQUESTS_ID_15 = 15, + AXI0_READ_REQUESTS_TOTAL = 16, + AXI1_READ_REQUESTS_TOTAL = 17, + AXI2_READ_REQUESTS_TOTAL = 18, + AXI3_READ_REQUESTS_TOTAL = 19, + AXI_READ_REQUESTS_TOTAL = 20, + AXI_WRITE_REQUESTS_ID_0 = 21, + AXI_WRITE_REQUESTS_ID_1 = 22, + AXI_WRITE_REQUESTS_ID_2 = 23, + AXI_WRITE_REQUESTS_ID_3 = 24, + AXI_WRITE_REQUESTS_ID_4 = 25, + AXI_WRITE_REQUESTS_ID_5 = 26, + AXI_WRITE_REQUESTS_ID_6 = 27, + AXI_WRITE_REQUESTS_ID_7 = 28, + AXI_WRITE_REQUESTS_ID_8 = 29, + AXI_WRITE_REQUESTS_ID_9 = 30, + AXI_WRITE_REQUESTS_ID_10 = 31, + AXI_WRITE_REQUESTS_ID_11 = 32, + AXI_WRITE_REQUESTS_ID_12 = 33, + AXI_WRITE_REQUESTS_ID_13 = 34, + AXI_WRITE_REQUESTS_ID_14 = 35, + AXI_WRITE_REQUESTS_ID_15 = 36, + AXI0_WRITE_REQUESTS_TOTAL = 37, + AXI1_WRITE_REQUESTS_TOTAL = 38, + AXI2_WRITE_REQUESTS_TOTAL = 39, + AXI3_WRITE_REQUESTS_TOTAL = 40, + AXI_WRITE_REQUESTS_TOTAL = 41, + AXI_TOTAL_REQUESTS = 42, + AXI_READ_DATA_BEATS_ID_0 = 43, + AXI_READ_DATA_BEATS_ID_1 = 44, + AXI_READ_DATA_BEATS_ID_2 = 45, + AXI_READ_DATA_BEATS_ID_3 = 46, + AXI_READ_DATA_BEATS_ID_4 = 47, + AXI_READ_DATA_BEATS_ID_5 = 48, + AXI_READ_DATA_BEATS_ID_6 = 49, + AXI_READ_DATA_BEATS_ID_7 = 50, + AXI_READ_DATA_BEATS_ID_8 = 51, + AXI_READ_DATA_BEATS_ID_9 = 52, + AXI_READ_DATA_BEATS_ID_10 = 53, + AXI_READ_DATA_BEATS_ID_11 = 54, + AXI_READ_DATA_BEATS_ID_12 = 55, + AXI_READ_DATA_BEATS_ID_13 = 56, + AXI_READ_DATA_BEATS_ID_14 = 57, + AXI_READ_DATA_BEATS_ID_15 = 58, + AXI0_READ_DATA_BEATS_TOTAL = 59, + AXI1_READ_DATA_BEATS_TOTAL = 60, + AXI2_READ_DATA_BEATS_TOTAL = 61, + AXI3_READ_DATA_BEATS_TOTAL = 62, + AXI_READ_DATA_BEATS_TOTAL = 63, + AXI_WRITE_DATA_BEATS_ID_0 = 64, + AXI_WRITE_DATA_BEATS_ID_1 = 65, + AXI_WRITE_DATA_BEATS_ID_2 = 66, + AXI_WRITE_DATA_BEATS_ID_3 = 67, + AXI_WRITE_DATA_BEATS_ID_4 = 68, + AXI_WRITE_DATA_BEATS_ID_5 = 69, + AXI_WRITE_DATA_BEATS_ID_6 = 70, + AXI_WRITE_DATA_BEATS_ID_7 = 71, + AXI_WRITE_DATA_BEATS_ID_8 = 72, + AXI_WRITE_DATA_BEATS_ID_9 = 73, + AXI_WRITE_DATA_BEATS_ID_10 = 74, + AXI_WRITE_DATA_BEATS_ID_11 = 75, + AXI_WRITE_DATA_BEATS_ID_12 = 76, + AXI_WRITE_DATA_BEATS_ID_13 = 77, + AXI_WRITE_DATA_BEATS_ID_14 = 78, + AXI_WRITE_DATA_BEATS_ID_15 = 79, + AXI0_WRITE_DATA_BEATS_TOTAL = 80, + AXI1_WRITE_DATA_BEATS_TOTAL = 81, + AXI2_WRITE_DATA_BEATS_TOTAL = 82, + AXI3_WRITE_DATA_BEATS_TOTAL = 83, + AXI_WRITE_DATA_BEATS_TOTAL = 84, + AXI_DATA_BEATS_TOTAL = 85, + CYCLES_HELD_OFF_ID_0 = 86, + CYCLES_HELD_OFF_ID_1 = 87, + CYCLES_HELD_OFF_ID_2 = 88, + CYCLES_HELD_OFF_ID_3 = 89, + CYCLES_HELD_OFF_ID_4 = 90, + CYCLES_HELD_OFF_ID_5 = 91, + CYCLES_HELD_OFF_ID_6 = 92, + CYCLES_HELD_OFF_ID_7 = 93, + CYCLES_HELD_OFF_ID_8 = 94, + CYCLES_HELD_OFF_ID_9 = 95, + CYCLES_HELD_OFF_ID_10 = 96, + CYCLES_HELD_OFF_ID_11 = 97, + CYCLES_HELD_OFF_ID_12 = 98, + CYCLES_HELD_OFF_ID_13 = 99, + CYCLES_HELD_OFF_ID_14 = 100, + CYCLES_HELD_OFF_ID_15 = 101, + AXI_READ_REQUEST_HELD_OFF = 102, + AXI_WRITE_REQUEST_HELD_OFF = 103, + AXI_REQUEST_HELD_OFF = 104, + AXI_WRITE_DATA_HELD_OFF = 105, + OCMEM_AXI_READ_REQUEST_HELD_OFF = 106, + OCMEM_AXI_WRITE_REQUEST_HELD_OFF = 107, + OCMEM_AXI_REQUEST_HELD_OFF = 108, + OCMEM_AXI_WRITE_DATA_HELD_OFF = 109, + ELAPSED_CYCLES_DDR = 110, + ELAPSED_CYCLES_OCMEM = 111, +}; + +enum a4xx_vfd_perfcounter_select { + VFD_UCHE_BYTE_FETCHED = 0, + VFD_UCHE_TRANS = 1, + VFD_FETCH_INSTRUCTIONS = 3, + VFD_BUSY_CYCLES = 5, + VFD_STALL_CYCLES_UCHE = 6, + VFD_STALL_CYCLES_HLSQ = 7, + VFD_STALL_CYCLES_VPC_BYPASS = 8, + VFD_STALL_CYCLES_VPC_ALLOC = 9, + VFD_MODE_0_FIBERS = 13, + VFD_MODE_1_FIBERS = 14, + VFD_MODE_2_FIBERS = 15, + VFD_MODE_3_FIBERS = 16, + VFD_MODE_4_FIBERS = 17, + VFD_BFIFO_STALL = 18, + VFD_NUM_VERTICES_TOTAL = 19, + VFD_PACKER_FULL = 20, + VFD_UCHE_REQUEST_FIFO_FULL = 21, + VFD_STARVE_CYCLES_PC = 22, + VFD_STARVE_CYCLES_UCHE = 23, +}; + +enum a4xx_vpc_perfcounter_select { + VPC_SP_LM_COMPONENTS = 2, + VPC_SP0_LM_BYTES = 3, + VPC_SP1_LM_BYTES = 4, + VPC_SP2_LM_BYTES = 5, + VPC_SP3_LM_BYTES = 6, + VPC_WORKING_CYCLES = 7, + VPC_STALL_CYCLES_LM = 8, + VPC_STARVE_CYCLES_RAS = 9, + VPC_STREAMOUT_CYCLES = 10, + VPC_UCHE_TRANSACTIONS = 12, + VPC_STALL_CYCLES_UCHE = 13, + VPC_BUSY_CYCLES = 14, + VPC_STARVE_CYCLES_SP = 15, +}; + +enum a4xx_vsc_perfcounter_select { + VSC_BUSY_CYCLES = 0, + VSC_WORKING_CYCLES = 1, + VSC_STALL_CYCLES_UCHE = 2, + VSC_STARVE_CYCLES_RAS = 3, + VSC_EOT_NUM = 4, +}; + enum a4xx_tex_filter { A4XX_TEX_NEAREST = 0, A4XX_TEX_LINEAR = 1, @@ -326,6 +897,12 @@ static inline uint32_t A4XX_CGC_HLSQ_EARLY_CYC(uint32_t val) #define REG_A4XX_RB_PERFCTR_RB_SEL_7 0x00000cce +#define REG_A4XX_RB_PERFCTR_CCU_SEL_0 0x00000ccf + +#define REG_A4XX_RB_PERFCTR_CCU_SEL_1 0x00000cd0 + +#define REG_A4XX_RB_PERFCTR_CCU_SEL_2 0x00000cd1 + #define REG_A4XX_RB_PERFCTR_CCU_SEL_3 0x00000cd2 #define REG_A4XX_RB_FRAME_BUFFER_DIMENSION 0x00000ce0 @@ -400,8 +977,13 @@ static inline uint32_t REG_A4XX_RB_MRT_CONTROL(uint32_t i0) { return 0x000020a4 #define A4XX_RB_MRT_CONTROL_READ_DEST_ENABLE 0x00000008 #define A4XX_RB_MRT_CONTROL_BLEND 0x00000010 #define A4XX_RB_MRT_CONTROL_BLEND2 0x00000020 -#define A4XX_RB_MRT_CONTROL_FASTCLEAR 0x00000400 -#define A4XX_RB_MRT_CONTROL_B11 0x00000800 +#define A4XX_RB_MRT_CONTROL_ROP_ENABLE 0x00000040 +#define A4XX_RB_MRT_CONTROL_ROP_CODE__MASK 0x00000f00 +#define A4XX_RB_MRT_CONTROL_ROP_CODE__SHIFT 8 +static inline uint32_t A4XX_RB_MRT_CONTROL_ROP_CODE(enum a3xx_rop_code val) +{ + return ((val) << A4XX_RB_MRT_CONTROL_ROP_CODE__SHIFT) & A4XX_RB_MRT_CONTROL_ROP_CODE__MASK; +} #define A4XX_RB_MRT_CONTROL_COMPONENT_ENABLE__MASK 0x0f000000 #define A4XX_RB_MRT_CONTROL_COMPONENT_ENABLE__SHIFT 24 static inline uint32_t A4XX_RB_MRT_CONTROL_COMPONENT_ENABLE(uint32_t val) @@ -490,8 +1072,8 @@ static inline uint32_t A4XX_RB_MRT_BLEND_CONTROL_ALPHA_DEST_FACTOR(enum adreno_r return ((val) << A4XX_RB_MRT_BLEND_CONTROL_ALPHA_DEST_FACTOR__SHIFT) & A4XX_RB_MRT_BLEND_CONTROL_ALPHA_DEST_FACTOR__MASK; } -#define REG_A4XX_RB_BLEND_RED 0x000020f3 -#define A4XX_RB_BLEND_RED_UINT__MASK 0x00007fff +#define REG_A4XX_RB_BLEND_RED 0x000020f0 +#define A4XX_RB_BLEND_RED_UINT__MASK 0x0000ffff #define A4XX_RB_BLEND_RED_UINT__SHIFT 0 static inline uint32_t A4XX_RB_BLEND_RED_UINT(uint32_t val) { @@ -504,8 +1086,16 @@ static inline uint32_t A4XX_RB_BLEND_RED_FLOAT(float val) return ((util_float_to_half(val)) << A4XX_RB_BLEND_RED_FLOAT__SHIFT) & A4XX_RB_BLEND_RED_FLOAT__MASK; } -#define REG_A4XX_RB_BLEND_GREEN 0x000020f4 -#define A4XX_RB_BLEND_GREEN_UINT__MASK 0x00007fff +#define REG_A4XX_RB_BLEND_RED_F32 0x000020f1 +#define A4XX_RB_BLEND_RED_F32__MASK 0xffffffff +#define A4XX_RB_BLEND_RED_F32__SHIFT 0 +static inline uint32_t A4XX_RB_BLEND_RED_F32(float val) +{ + return ((fui(val)) << A4XX_RB_BLEND_RED_F32__SHIFT) & A4XX_RB_BLEND_RED_F32__MASK; +} + +#define REG_A4XX_RB_BLEND_GREEN 0x000020f2 +#define A4XX_RB_BLEND_GREEN_UINT__MASK 0x0000ffff #define A4XX_RB_BLEND_GREEN_UINT__SHIFT 0 static inline uint32_t A4XX_RB_BLEND_GREEN_UINT(uint32_t val) { @@ -518,8 +1108,16 @@ static inline uint32_t A4XX_RB_BLEND_GREEN_FLOAT(float val) return ((util_float_to_half(val)) << A4XX_RB_BLEND_GREEN_FLOAT__SHIFT) & A4XX_RB_BLEND_GREEN_FLOAT__MASK; } -#define REG_A4XX_RB_BLEND_BLUE 0x000020f5 -#define A4XX_RB_BLEND_BLUE_UINT__MASK 0x00007fff +#define REG_A4XX_RB_BLEND_GREEN_F32 0x000020f3 +#define A4XX_RB_BLEND_GREEN_F32__MASK 0xffffffff +#define A4XX_RB_BLEND_GREEN_F32__SHIFT 0 +static inline uint32_t A4XX_RB_BLEND_GREEN_F32(float val) +{ + return ((fui(val)) << A4XX_RB_BLEND_GREEN_F32__SHIFT) & A4XX_RB_BLEND_GREEN_F32__MASK; +} + +#define REG_A4XX_RB_BLEND_BLUE 0x000020f4 +#define A4XX_RB_BLEND_BLUE_UINT__MASK 0x0000ffff #define A4XX_RB_BLEND_BLUE_UINT__SHIFT 0 static inline uint32_t A4XX_RB_BLEND_BLUE_UINT(uint32_t val) { @@ -532,8 +1130,16 @@ static inline uint32_t A4XX_RB_BLEND_BLUE_FLOAT(float val) return ((util_float_to_half(val)) << A4XX_RB_BLEND_BLUE_FLOAT__SHIFT) & A4XX_RB_BLEND_BLUE_FLOAT__MASK; } +#define REG_A4XX_RB_BLEND_BLUE_F32 0x000020f5 +#define A4XX_RB_BLEND_BLUE_F32__MASK 0xffffffff +#define A4XX_RB_BLEND_BLUE_F32__SHIFT 0 +static inline uint32_t A4XX_RB_BLEND_BLUE_F32(float val) +{ + return ((fui(val)) << A4XX_RB_BLEND_BLUE_F32__SHIFT) & A4XX_RB_BLEND_BLUE_F32__MASK; +} + #define REG_A4XX_RB_BLEND_ALPHA 0x000020f6 -#define A4XX_RB_BLEND_ALPHA_UINT__MASK 0x00007fff +#define A4XX_RB_BLEND_ALPHA_UINT__MASK 0x0000ffff #define A4XX_RB_BLEND_ALPHA_UINT__SHIFT 0 static inline uint32_t A4XX_RB_BLEND_ALPHA_UINT(uint32_t val) { @@ -546,6 +1152,14 @@ static inline uint32_t A4XX_RB_BLEND_ALPHA_FLOAT(float val) return ((util_float_to_half(val)) << A4XX_RB_BLEND_ALPHA_FLOAT__SHIFT) & A4XX_RB_BLEND_ALPHA_FLOAT__MASK; } +#define REG_A4XX_RB_BLEND_ALPHA_F32 0x000020f7 +#define A4XX_RB_BLEND_ALPHA_F32__MASK 0xffffffff +#define A4XX_RB_BLEND_ALPHA_F32__SHIFT 0 +static inline uint32_t A4XX_RB_BLEND_ALPHA_F32(float val) +{ + return ((fui(val)) << A4XX_RB_BLEND_ALPHA_F32__SHIFT) & A4XX_RB_BLEND_ALPHA_F32__MASK; +} + #define REG_A4XX_RB_ALPHA_CONTROL 0x000020f8 #define A4XX_RB_ALPHA_CONTROL_ALPHA_REF__MASK 0x000000ff #define A4XX_RB_ALPHA_CONTROL_ALPHA_REF__SHIFT 0 @@ -568,7 +1182,7 @@ static inline uint32_t A4XX_RB_FS_OUTPUT_ENABLE_BLEND(uint32_t val) { return ((val) << A4XX_RB_FS_OUTPUT_ENABLE_BLEND__SHIFT) & A4XX_RB_FS_OUTPUT_ENABLE_BLEND__MASK; } -#define A4XX_RB_FS_OUTPUT_FAST_CLEAR 0x00000100 +#define A4XX_RB_FS_OUTPUT_INDEPENDENT_BLEND 0x00000100 #define A4XX_RB_FS_OUTPUT_SAMPLE_MASK__MASK 0xffff0000 #define A4XX_RB_FS_OUTPUT_SAMPLE_MASK__SHIFT 16 static inline uint32_t A4XX_RB_FS_OUTPUT_SAMPLE_MASK(uint32_t val) @@ -736,6 +1350,7 @@ static inline uint32_t A4XX_RB_DEPTH_CONTROL_ZFUNC(enum adreno_compare_func val) } #define A4XX_RB_DEPTH_CONTROL_BF_ENABLE 0x00000080 #define A4XX_RB_DEPTH_CONTROL_EARLY_Z_DISABLE 0x00010000 +#define A4XX_RB_DEPTH_CONTROL_FORCE_FRAGZ_TO_FS 0x00020000 #define A4XX_RB_DEPTH_CONTROL_Z_TEST_ENABLE 0x80000000 #define REG_A4XX_RB_DEPTH_CLEAR 0x00002102 @@ -996,8 +1611,386 @@ static inline uint32_t REG_A4XX_RBBM_CLOCK_DELAY_TP_REG(uint32_t i0) { return 0x #define REG_A4XX_RBBM_CFG_DEBBUS_SEL_D 0x0000004d +#define REG_A4XX_RBBM_POWER_CNTL_IP 0x00000098 +#define A4XX_RBBM_POWER_CNTL_IP_SW_COLLAPSE 0x00000001 +#define A4XX_RBBM_POWER_CNTL_IP_SP_TP_PWR_ON 0x00100000 + #define REG_A4XX_RBBM_PERFCTR_CP_0_LO 0x0000009c +#define REG_A4XX_RBBM_PERFCTR_CP_0_HI 0x0000009d + +#define REG_A4XX_RBBM_PERFCTR_CP_1_LO 0x0000009e + +#define REG_A4XX_RBBM_PERFCTR_CP_1_HI 0x0000009f + +#define REG_A4XX_RBBM_PERFCTR_CP_2_LO 0x000000a0 + +#define REG_A4XX_RBBM_PERFCTR_CP_2_HI 0x000000a1 + +#define REG_A4XX_RBBM_PERFCTR_CP_3_LO 0x000000a2 + +#define REG_A4XX_RBBM_PERFCTR_CP_3_HI 0x000000a3 + +#define REG_A4XX_RBBM_PERFCTR_CP_4_LO 0x000000a4 + +#define REG_A4XX_RBBM_PERFCTR_CP_4_HI 0x000000a5 + +#define REG_A4XX_RBBM_PERFCTR_CP_5_LO 0x000000a6 + +#define REG_A4XX_RBBM_PERFCTR_CP_5_HI 0x000000a7 + +#define REG_A4XX_RBBM_PERFCTR_CP_6_LO 0x000000a8 + +#define REG_A4XX_RBBM_PERFCTR_CP_6_HI 0x000000a9 + +#define REG_A4XX_RBBM_PERFCTR_CP_7_LO 0x000000aa + +#define REG_A4XX_RBBM_PERFCTR_CP_7_HI 0x000000ab + +#define REG_A4XX_RBBM_PERFCTR_RBBM_0_LO 0x000000ac + +#define REG_A4XX_RBBM_PERFCTR_RBBM_0_HI 0x000000ad + +#define REG_A4XX_RBBM_PERFCTR_RBBM_1_LO 0x000000ae + +#define REG_A4XX_RBBM_PERFCTR_RBBM_1_HI 0x000000af + +#define REG_A4XX_RBBM_PERFCTR_RBBM_2_LO 0x000000b0 + +#define REG_A4XX_RBBM_PERFCTR_RBBM_2_HI 0x000000b1 + +#define REG_A4XX_RBBM_PERFCTR_RBBM_3_LO 0x000000b2 + +#define REG_A4XX_RBBM_PERFCTR_RBBM_3_HI 0x000000b3 + +#define REG_A4XX_RBBM_PERFCTR_PC_0_LO 0x000000b4 + +#define REG_A4XX_RBBM_PERFCTR_PC_0_HI 0x000000b5 + +#define REG_A4XX_RBBM_PERFCTR_PC_1_LO 0x000000b6 + +#define REG_A4XX_RBBM_PERFCTR_PC_1_HI 0x000000b7 + +#define REG_A4XX_RBBM_PERFCTR_PC_2_LO 0x000000b8 + +#define REG_A4XX_RBBM_PERFCTR_PC_2_HI 0x000000b9 + +#define REG_A4XX_RBBM_PERFCTR_PC_3_LO 0x000000ba + +#define REG_A4XX_RBBM_PERFCTR_PC_3_HI 0x000000bb + +#define REG_A4XX_RBBM_PERFCTR_PC_4_LO 0x000000bc + +#define REG_A4XX_RBBM_PERFCTR_PC_4_HI 0x000000bd + +#define REG_A4XX_RBBM_PERFCTR_PC_5_LO 0x000000be + +#define REG_A4XX_RBBM_PERFCTR_PC_5_HI 0x000000bf + +#define REG_A4XX_RBBM_PERFCTR_PC_6_LO 0x000000c0 + +#define REG_A4XX_RBBM_PERFCTR_PC_6_HI 0x000000c1 + +#define REG_A4XX_RBBM_PERFCTR_PC_7_LO 0x000000c2 + +#define REG_A4XX_RBBM_PERFCTR_PC_7_HI 0x000000c3 + +#define REG_A4XX_RBBM_PERFCTR_VFD_0_LO 0x000000c4 + +#define REG_A4XX_RBBM_PERFCTR_VFD_0_HI 0x000000c5 + +#define REG_A4XX_RBBM_PERFCTR_VFD_1_LO 0x000000c6 + +#define REG_A4XX_RBBM_PERFCTR_VFD_1_HI 0x000000c7 + +#define REG_A4XX_RBBM_PERFCTR_VFD_2_LO 0x000000c8 + +#define REG_A4XX_RBBM_PERFCTR_VFD_2_HI 0x000000c9 + +#define REG_A4XX_RBBM_PERFCTR_VFD_3_LO 0x000000ca + +#define REG_A4XX_RBBM_PERFCTR_VFD_3_HI 0x000000cb + +#define REG_A4XX_RBBM_PERFCTR_VFD_4_LO 0x000000cc + +#define REG_A4XX_RBBM_PERFCTR_VFD_4_HI 0x000000cd + +#define REG_A4XX_RBBM_PERFCTR_VFD_5_LO 0x000000ce + +#define REG_A4XX_RBBM_PERFCTR_VFD_5_HI 0x000000cf + +#define REG_A4XX_RBBM_PERFCTR_VFD_6_LO 0x000000d0 + +#define REG_A4XX_RBBM_PERFCTR_VFD_6_HI 0x000000d1 + +#define REG_A4XX_RBBM_PERFCTR_VFD_7_LO 0x000000d2 + +#define REG_A4XX_RBBM_PERFCTR_VFD_7_HI 0x000000d3 + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_0_LO 0x000000d4 + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_0_HI 0x000000d5 + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_1_LO 0x000000d6 + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_1_HI 0x000000d7 + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_2_LO 0x000000d8 + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_2_HI 0x000000d9 + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_3_LO 0x000000da + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_3_HI 0x000000db + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_4_LO 0x000000dc + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_4_HI 0x000000dd + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_5_LO 0x000000de + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_5_HI 0x000000df + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_6_LO 0x000000e0 + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_6_HI 0x000000e1 + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_7_LO 0x000000e2 + +#define REG_A4XX_RBBM_PERFCTR_HLSQ_7_HI 0x000000e3 + +#define REG_A4XX_RBBM_PERFCTR_VPC_0_LO 0x000000e4 + +#define REG_A4XX_RBBM_PERFCTR_VPC_0_HI 0x000000e5 + +#define REG_A4XX_RBBM_PERFCTR_VPC_1_LO 0x000000e6 + +#define REG_A4XX_RBBM_PERFCTR_VPC_1_HI 0x000000e7 + +#define REG_A4XX_RBBM_PERFCTR_VPC_2_LO 0x000000e8 + +#define REG_A4XX_RBBM_PERFCTR_VPC_2_HI 0x000000e9 + +#define REG_A4XX_RBBM_PERFCTR_VPC_3_LO 0x000000ea + +#define REG_A4XX_RBBM_PERFCTR_VPC_3_HI 0x000000eb + +#define REG_A4XX_RBBM_PERFCTR_CCU_0_LO 0x000000ec + +#define REG_A4XX_RBBM_PERFCTR_CCU_0_HI 0x000000ed + +#define REG_A4XX_RBBM_PERFCTR_CCU_1_LO 0x000000ee + +#define REG_A4XX_RBBM_PERFCTR_CCU_1_HI 0x000000ef + +#define REG_A4XX_RBBM_PERFCTR_CCU_2_LO 0x000000f0 + +#define REG_A4XX_RBBM_PERFCTR_CCU_2_HI 0x000000f1 + +#define REG_A4XX_RBBM_PERFCTR_CCU_3_LO 0x000000f2 + +#define REG_A4XX_RBBM_PERFCTR_CCU_3_HI 0x000000f3 + +#define REG_A4XX_RBBM_PERFCTR_TSE_0_LO 0x000000f4 + +#define REG_A4XX_RBBM_PERFCTR_TSE_0_HI 0x000000f5 + +#define REG_A4XX_RBBM_PERFCTR_TSE_1_LO 0x000000f6 + +#define REG_A4XX_RBBM_PERFCTR_TSE_1_HI 0x000000f7 + +#define REG_A4XX_RBBM_PERFCTR_TSE_2_LO 0x000000f8 + +#define REG_A4XX_RBBM_PERFCTR_TSE_2_HI 0x000000f9 + +#define REG_A4XX_RBBM_PERFCTR_TSE_3_LO 0x000000fa + +#define REG_A4XX_RBBM_PERFCTR_TSE_3_HI 0x000000fb + +#define REG_A4XX_RBBM_PERFCTR_RAS_0_LO 0x000000fc + +#define REG_A4XX_RBBM_PERFCTR_RAS_0_HI 0x000000fd + +#define REG_A4XX_RBBM_PERFCTR_RAS_1_LO 0x000000fe + +#define REG_A4XX_RBBM_PERFCTR_RAS_1_HI 0x000000ff + +#define REG_A4XX_RBBM_PERFCTR_RAS_2_LO 0x00000100 + +#define REG_A4XX_RBBM_PERFCTR_RAS_2_HI 0x00000101 + +#define REG_A4XX_RBBM_PERFCTR_RAS_3_LO 0x00000102 + +#define REG_A4XX_RBBM_PERFCTR_RAS_3_HI 0x00000103 + +#define REG_A4XX_RBBM_PERFCTR_UCHE_0_LO 0x00000104 + +#define REG_A4XX_RBBM_PERFCTR_UCHE_0_HI 0x00000105 + +#define REG_A4XX_RBBM_PERFCTR_UCHE_1_LO 0x00000106 + +#define REG_A4XX_RBBM_PERFCTR_UCHE_1_HI 0x00000107 + +#define REG_A4XX_RBBM_PERFCTR_UCHE_2_LO 0x00000108 + +#define REG_A4XX_RBBM_PERFCTR_UCHE_2_HI 0x00000109 + +#define REG_A4XX_RBBM_PERFCTR_UCHE_3_LO 0x0000010a + +#define REG_A4XX_RBBM_PERFCTR_UCHE_3_HI 0x0000010b + +#define REG_A4XX_RBBM_PERFCTR_UCHE_4_LO 0x0000010c + +#define REG_A4XX_RBBM_PERFCTR_UCHE_4_HI 0x0000010d + +#define REG_A4XX_RBBM_PERFCTR_UCHE_5_LO 0x0000010e + +#define REG_A4XX_RBBM_PERFCTR_UCHE_5_HI 0x0000010f + +#define REG_A4XX_RBBM_PERFCTR_UCHE_6_LO 0x00000110 + +#define REG_A4XX_RBBM_PERFCTR_UCHE_6_HI 0x00000111 + +#define REG_A4XX_RBBM_PERFCTR_UCHE_7_LO 0x00000112 + +#define REG_A4XX_RBBM_PERFCTR_UCHE_7_HI 0x00000113 + +#define REG_A4XX_RBBM_PERFCTR_TP_0_LO 0x00000114 + +#define REG_A4XX_RBBM_PERFCTR_TP_0_HI 0x00000115 + +#define REG_A4XX_RBBM_PERFCTR_TP_0_LO 0x00000114 + +#define REG_A4XX_RBBM_PERFCTR_TP_0_HI 0x00000115 + +#define REG_A4XX_RBBM_PERFCTR_TP_1_LO 0x00000116 + +#define REG_A4XX_RBBM_PERFCTR_TP_1_HI 0x00000117 + +#define REG_A4XX_RBBM_PERFCTR_TP_2_LO 0x00000118 + +#define REG_A4XX_RBBM_PERFCTR_TP_2_HI 0x00000119 + +#define REG_A4XX_RBBM_PERFCTR_TP_3_LO 0x0000011a + +#define REG_A4XX_RBBM_PERFCTR_TP_3_HI 0x0000011b + +#define REG_A4XX_RBBM_PERFCTR_TP_4_LO 0x0000011c + +#define REG_A4XX_RBBM_PERFCTR_TP_4_HI 0x0000011d + +#define REG_A4XX_RBBM_PERFCTR_TP_5_LO 0x0000011e + +#define REG_A4XX_RBBM_PERFCTR_TP_5_HI 0x0000011f + +#define REG_A4XX_RBBM_PERFCTR_TP_6_LO 0x00000120 + +#define REG_A4XX_RBBM_PERFCTR_TP_6_HI 0x00000121 + +#define REG_A4XX_RBBM_PERFCTR_TP_7_LO 0x00000122 + +#define REG_A4XX_RBBM_PERFCTR_TP_7_HI 0x00000123 + +#define REG_A4XX_RBBM_PERFCTR_SP_0_LO 0x00000124 + +#define REG_A4XX_RBBM_PERFCTR_SP_0_HI 0x00000125 + +#define REG_A4XX_RBBM_PERFCTR_SP_1_LO 0x00000126 + +#define REG_A4XX_RBBM_PERFCTR_SP_1_HI 0x00000127 + +#define REG_A4XX_RBBM_PERFCTR_SP_2_LO 0x00000128 + +#define REG_A4XX_RBBM_PERFCTR_SP_2_HI 0x00000129 + +#define REG_A4XX_RBBM_PERFCTR_SP_3_LO 0x0000012a + +#define REG_A4XX_RBBM_PERFCTR_SP_3_HI 0x0000012b + +#define REG_A4XX_RBBM_PERFCTR_SP_4_LO 0x0000012c + +#define REG_A4XX_RBBM_PERFCTR_SP_4_HI 0x0000012d + +#define REG_A4XX_RBBM_PERFCTR_SP_5_LO 0x0000012e + +#define REG_A4XX_RBBM_PERFCTR_SP_5_HI 0x0000012f + +#define REG_A4XX_RBBM_PERFCTR_SP_6_LO 0x00000130 + +#define REG_A4XX_RBBM_PERFCTR_SP_6_HI 0x00000131 + +#define REG_A4XX_RBBM_PERFCTR_SP_7_LO 0x00000132 + +#define REG_A4XX_RBBM_PERFCTR_SP_7_HI 0x00000133 + +#define REG_A4XX_RBBM_PERFCTR_SP_8_LO 0x00000134 + +#define REG_A4XX_RBBM_PERFCTR_SP_8_HI 0x00000135 + +#define REG_A4XX_RBBM_PERFCTR_SP_9_LO 0x00000136 + +#define REG_A4XX_RBBM_PERFCTR_SP_9_HI 0x00000137 + +#define REG_A4XX_RBBM_PERFCTR_SP_10_LO 0x00000138 + +#define REG_A4XX_RBBM_PERFCTR_SP_10_HI 0x00000139 + +#define REG_A4XX_RBBM_PERFCTR_SP_11_LO 0x0000013a + +#define REG_A4XX_RBBM_PERFCTR_SP_11_HI 0x0000013b + +#define REG_A4XX_RBBM_PERFCTR_RB_0_LO 0x0000013c + +#define REG_A4XX_RBBM_PERFCTR_RB_0_HI 0x0000013d + +#define REG_A4XX_RBBM_PERFCTR_RB_1_LO 0x0000013e + +#define REG_A4XX_RBBM_PERFCTR_RB_1_HI 0x0000013f + +#define REG_A4XX_RBBM_PERFCTR_RB_2_LO 0x00000140 + +#define REG_A4XX_RBBM_PERFCTR_RB_2_HI 0x00000141 + +#define REG_A4XX_RBBM_PERFCTR_RB_3_LO 0x00000142 + +#define REG_A4XX_RBBM_PERFCTR_RB_3_HI 0x00000143 + +#define REG_A4XX_RBBM_PERFCTR_RB_4_LO 0x00000144 + +#define REG_A4XX_RBBM_PERFCTR_RB_4_HI 0x00000145 + +#define REG_A4XX_RBBM_PERFCTR_RB_5_LO 0x00000146 + +#define REG_A4XX_RBBM_PERFCTR_RB_5_HI 0x00000147 + +#define REG_A4XX_RBBM_PERFCTR_RB_6_LO 0x00000148 + +#define REG_A4XX_RBBM_PERFCTR_RB_6_HI 0x00000149 + +#define REG_A4XX_RBBM_PERFCTR_RB_7_LO 0x0000014a + +#define REG_A4XX_RBBM_PERFCTR_RB_7_HI 0x0000014b + +#define REG_A4XX_RBBM_PERFCTR_VSC_0_LO 0x0000014c + +#define REG_A4XX_RBBM_PERFCTR_VSC_0_HI 0x0000014d + +#define REG_A4XX_RBBM_PERFCTR_VSC_1_LO 0x0000014e + +#define REG_A4XX_RBBM_PERFCTR_VSC_1_HI 0x0000014f + +#define REG_A4XX_RBBM_PERFCTR_PWR_0_LO 0x00000166 + +#define REG_A4XX_RBBM_PERFCTR_PWR_0_HI 0x00000167 + +#define REG_A4XX_RBBM_PERFCTR_PWR_1_LO 0x00000168 + +#define REG_A4XX_RBBM_PERFCTR_PWR_1_HI 0x00000169 + +#define REG_A4XX_RBBM_ALWAYSON_COUNTER_LO 0x0000016e + +#define REG_A4XX_RBBM_ALWAYSON_COUNTER_HI 0x0000016f + static inline uint32_t REG_A4XX_RBBM_CLOCK_CTL_SP(uint32_t i0) { return 0x00000068 + 0x1*i0; } static inline uint32_t REG_A4XX_RBBM_CLOCK_CTL_SP_REG(uint32_t i0) { return 0x00000068 + 0x1*i0; } @@ -1046,6 +2039,10 @@ static inline uint32_t REG_A4XX_RBBM_CLOCK_DELAY_RB_MARB_CCU_L1(uint32_t i0) { r static inline uint32_t REG_A4XX_RBBM_CLOCK_DELAY_RB_MARB_CCU_L1_REG(uint32_t i0) { return 0x0000008e + 0x1*i0; } +#define REG_A4XX_RBBM_SP_REGFILE_SLEEP_CNTL_0 0x00000099 + +#define REG_A4XX_RBBM_SP_REGFILE_SLEEP_CNTL_1 0x0000009a + #define REG_A4XX_RBBM_PERFCTR_PWR_1_LO 0x00000168 #define REG_A4XX_RBBM_PERFCTR_CTL 0x00000170 @@ -1060,6 +2057,14 @@ static inline uint32_t REG_A4XX_RBBM_CLOCK_DELAY_RB_MARB_CCU_L1_REG(uint32_t i0) #define REG_A4XX_RBBM_PERFCTR_LOAD_VALUE_HI 0x00000175 +#define REG_A4XX_RBBM_PERFCTR_RBBM_SEL_0 0x00000176 + +#define REG_A4XX_RBBM_PERFCTR_RBBM_SEL_1 0x00000177 + +#define REG_A4XX_RBBM_PERFCTR_RBBM_SEL_2 0x00000178 + +#define REG_A4XX_RBBM_PERFCTR_RBBM_SEL_3 0x00000179 + #define REG_A4XX_RBBM_GPU_BUSY_MASKED 0x0000017a #define REG_A4XX_RBBM_INT_0_STATUS 0x0000017d @@ -1099,6 +2104,11 @@ static inline uint32_t REG_A4XX_RBBM_CLOCK_DELAY_RB_MARB_CCU_L1_REG(uint32_t i0) #define REG_A4XX_RBBM_INTERFACE_RRDY_STATUS5 0x0000019f +#define REG_A4XX_RBBM_POWER_STATUS 0x000001b0 +#define A4XX_RBBM_POWER_STATUS_SP_TP_PWR_ON 0x00100000 + +#define REG_A4XX_RBBM_WAIT_IDLE_CLOCKS_CTL2 0x000001b8 + #define REG_A4XX_CP_SCRATCH_UMASK 0x00000228 #define REG_A4XX_CP_SCRATCH_ADDR 0x00000229 @@ -1191,6 +2201,20 @@ static inline uint32_t REG_A4XX_CP_PROTECT_REG(uint32_t i0) { return 0x00000240 #define REG_A4XX_CP_PERFCTR_CP_SEL_0 0x00000500 +#define REG_A4XX_CP_PERFCTR_CP_SEL_1 0x00000501 + +#define REG_A4XX_CP_PERFCTR_CP_SEL_2 0x00000502 + +#define REG_A4XX_CP_PERFCTR_CP_SEL_3 0x00000503 + +#define REG_A4XX_CP_PERFCTR_CP_SEL_4 0x00000504 + +#define REG_A4XX_CP_PERFCTR_CP_SEL_5 0x00000505 + +#define REG_A4XX_CP_PERFCTR_CP_SEL_6 0x00000506 + +#define REG_A4XX_CP_PERFCTR_CP_SEL_7 0x00000507 + #define REG_A4XX_CP_PERFCOMBINER_SELECT 0x0000050b static inline uint32_t REG_A4XX_CP_SCRATCH(uint32_t i0) { return 0x00000578 + 0x1*i0; } @@ -1201,6 +2225,28 @@ static inline uint32_t REG_A4XX_CP_SCRATCH_REG(uint32_t i0) { return 0x00000578 #define REG_A4XX_SP_MODE_CONTROL 0x00000ec3 +#define REG_A4XX_SP_PERFCTR_SP_SEL_0 0x00000ec4 + +#define REG_A4XX_SP_PERFCTR_SP_SEL_1 0x00000ec5 + +#define REG_A4XX_SP_PERFCTR_SP_SEL_2 0x00000ec6 + +#define REG_A4XX_SP_PERFCTR_SP_SEL_3 0x00000ec7 + +#define REG_A4XX_SP_PERFCTR_SP_SEL_4 0x00000ec8 + +#define REG_A4XX_SP_PERFCTR_SP_SEL_5 0x00000ec9 + +#define REG_A4XX_SP_PERFCTR_SP_SEL_6 0x00000eca + +#define REG_A4XX_SP_PERFCTR_SP_SEL_7 0x00000ecb + +#define REG_A4XX_SP_PERFCTR_SP_SEL_8 0x00000ecc + +#define REG_A4XX_SP_PERFCTR_SP_SEL_9 0x00000ecd + +#define REG_A4XX_SP_PERFCTR_SP_SEL_10 0x00000ece + #define REG_A4XX_SP_PERFCTR_SP_SEL_11 0x00000ecf #define REG_A4XX_SP_SP_CTRL_REG 0x000022c0 @@ -1699,6 +2745,12 @@ static inline uint32_t A4XX_SP_GS_OBJ_OFFSET_REG_SHADEROBJOFFSET(uint32_t val) #define REG_A4XX_VPC_DEBUG_ECO_CONTROL 0x00000e64 +#define REG_A4XX_VPC_PERFCTR_VPC_SEL_0 0x00000e65 + +#define REG_A4XX_VPC_PERFCTR_VPC_SEL_1 0x00000e66 + +#define REG_A4XX_VPC_PERFCTR_VPC_SEL_2 0x00000e67 + #define REG_A4XX_VPC_PERFCTR_VPC_SEL_3 0x00000e68 #define REG_A4XX_VPC_ATTR 0x00002140 @@ -1811,6 +2863,20 @@ static inline uint32_t REG_A4XX_VSC_PIPE_DATA_LENGTH_REG(uint32_t i0) { return 0 #define REG_A4XX_VFD_DEBUG_CONTROL 0x00000e40 +#define REG_A4XX_VFD_PERFCTR_VFD_SEL_0 0x00000e43 + +#define REG_A4XX_VFD_PERFCTR_VFD_SEL_1 0x00000e44 + +#define REG_A4XX_VFD_PERFCTR_VFD_SEL_2 0x00000e45 + +#define REG_A4XX_VFD_PERFCTR_VFD_SEL_3 0x00000e46 + +#define REG_A4XX_VFD_PERFCTR_VFD_SEL_4 0x00000e47 + +#define REG_A4XX_VFD_PERFCTR_VFD_SEL_5 0x00000e48 + +#define REG_A4XX_VFD_PERFCTR_VFD_SEL_6 0x00000e49 + #define REG_A4XX_VFD_PERFCTR_VFD_SEL_7 0x00000e4a #define REG_A4XX_VGT_CL_INITIATOR 0x000021d0 @@ -1967,6 +3033,20 @@ static inline uint32_t A4XX_VFD_DECODE_INSTR_SHIFTCNT(uint32_t val) #define REG_A4XX_TPL1_TP_MODE_CONTROL 0x00000f03 +#define REG_A4XX_TPL1_PERFCTR_TP_SEL_0 0x00000f04 + +#define REG_A4XX_TPL1_PERFCTR_TP_SEL_1 0x00000f05 + +#define REG_A4XX_TPL1_PERFCTR_TP_SEL_2 0x00000f06 + +#define REG_A4XX_TPL1_PERFCTR_TP_SEL_3 0x00000f07 + +#define REG_A4XX_TPL1_PERFCTR_TP_SEL_4 0x00000f08 + +#define REG_A4XX_TPL1_PERFCTR_TP_SEL_5 0x00000f09 + +#define REG_A4XX_TPL1_PERFCTR_TP_SEL_6 0x00000f0a + #define REG_A4XX_TPL1_PERFCTR_TP_SEL_7 0x00000f0b #define REG_A4XX_TPL1_TP_TEX_OFFSET 0x00002380 @@ -2021,9 +3101,23 @@ static inline uint32_t A4XX_TPL1_TP_TEX_COUNT_GS(uint32_t val) #define REG_A4XX_GRAS_PERFCTR_TSE_SEL_0 0x00000c88 +#define REG_A4XX_GRAS_PERFCTR_TSE_SEL_1 0x00000c89 + +#define REG_A4XX_GRAS_PERFCTR_TSE_SEL_2 0x00000c8a + #define REG_A4XX_GRAS_PERFCTR_TSE_SEL_3 0x00000c8b +#define REG_A4XX_GRAS_PERFCTR_RAS_SEL_0 0x00000c8c + +#define REG_A4XX_GRAS_PERFCTR_RAS_SEL_1 0x00000c8d + +#define REG_A4XX_GRAS_PERFCTR_RAS_SEL_2 0x00000c8e + +#define REG_A4XX_GRAS_PERFCTR_RAS_SEL_3 0x00000c8f + #define REG_A4XX_GRAS_CL_CLIP_CNTL 0x00002000 +#define A4XX_GRAS_CL_CLIP_CNTL_CLIP_DISABLE 0x00008000 +#define A4XX_GRAS_CL_CLIP_CNTL_ZERO_GB_SCALE_Z 0x00400000 #define REG_A4XX_GRAS_CLEAR_CNTL 0x00002003 #define A4XX_GRAS_CLEAR_CNTL_NOT_FASTCLEAR 0x00000001 @@ -2114,6 +3208,7 @@ static inline uint32_t A4XX_GRAS_SU_POINT_SIZE(float val) #define REG_A4XX_GRAS_ALPHA_CONTROL 0x00002073 #define A4XX_GRAS_ALPHA_CONTROL_ALPHA_TEST_ENABLE 0x00000004 +#define A4XX_GRAS_ALPHA_CONTROL_FORCE_FRAGZ_TO_FS 0x00000008 #define REG_A4XX_GRAS_SU_POLY_OFFSET_SCALE 0x00002074 #define A4XX_GRAS_SU_POLY_OFFSET_SCALE__MASK 0xffffffff @@ -2285,6 +3380,20 @@ static inline uint32_t A4XX_GRAS_SC_EXTENT_WINDOW_TL_Y(uint32_t val) #define REG_A4XX_UCHE_CACHE_WAYS_VFD 0x00000e8c +#define REG_A4XX_UCHE_PERFCTR_UCHE_SEL_0 0x00000e8e + +#define REG_A4XX_UCHE_PERFCTR_UCHE_SEL_1 0x00000e8f + +#define REG_A4XX_UCHE_PERFCTR_UCHE_SEL_2 0x00000e90 + +#define REG_A4XX_UCHE_PERFCTR_UCHE_SEL_3 0x00000e91 + +#define REG_A4XX_UCHE_PERFCTR_UCHE_SEL_4 0x00000e92 + +#define REG_A4XX_UCHE_PERFCTR_UCHE_SEL_5 0x00000e93 + +#define REG_A4XX_UCHE_PERFCTR_UCHE_SEL_6 0x00000e94 + #define REG_A4XX_UCHE_PERFCTR_UCHE_SEL_7 0x00000e95 #define REG_A4XX_HLSQ_TIMEOUT_THRESHOLD 0x00000e00 @@ -2295,6 +3404,22 @@ static inline uint32_t A4XX_GRAS_SC_EXTENT_WINDOW_TL_Y(uint32_t val) #define REG_A4XX_HLSQ_PERF_PIPE_MASK 0x00000e0e +#define REG_A4XX_HLSQ_PERFCTR_HLSQ_SEL_0 0x00000e06 + +#define REG_A4XX_HLSQ_PERFCTR_HLSQ_SEL_1 0x00000e07 + +#define REG_A4XX_HLSQ_PERFCTR_HLSQ_SEL_2 0x00000e08 + +#define REG_A4XX_HLSQ_PERFCTR_HLSQ_SEL_3 0x00000e09 + +#define REG_A4XX_HLSQ_PERFCTR_HLSQ_SEL_4 0x00000e0a + +#define REG_A4XX_HLSQ_PERFCTR_HLSQ_SEL_5 0x00000e0b + +#define REG_A4XX_HLSQ_PERFCTR_HLSQ_SEL_6 0x00000e0c + +#define REG_A4XX_HLSQ_PERFCTR_HLSQ_SEL_7 0x00000e0d + #define REG_A4XX_HLSQ_CONTROL_0_REG 0x000023c0 #define A4XX_HLSQ_CONTROL_0_REG_FSTHREADSIZE__MASK 0x00000010 #define A4XX_HLSQ_CONTROL_0_REG_FSTHREADSIZE__SHIFT 4 @@ -2549,6 +3674,18 @@ static inline uint32_t A4XX_HLSQ_GS_CONTROL_REG_INSTRLENGTH(uint32_t val) #define REG_A4XX_PC_PERFCTR_PC_SEL_0 0x00000d10 +#define REG_A4XX_PC_PERFCTR_PC_SEL_1 0x00000d11 + +#define REG_A4XX_PC_PERFCTR_PC_SEL_2 0x00000d12 + +#define REG_A4XX_PC_PERFCTR_PC_SEL_3 0x00000d13 + +#define REG_A4XX_PC_PERFCTR_PC_SEL_4 0x00000d14 + +#define REG_A4XX_PC_PERFCTR_PC_SEL_5 0x00000d15 + +#define REG_A4XX_PC_PERFCTR_PC_SEL_6 0x00000d16 + #define REG_A4XX_PC_PERFCTR_PC_SEL_7 0x00000d17 #define REG_A4XX_PC_BIN_BASE 0x000021c0 @@ -2564,7 +3701,20 @@ static inline uint32_t A4XX_PC_PRIM_VTX_CNTL_VAROUT(uint32_t val) #define A4XX_PC_PRIM_VTX_CNTL_PROVOKING_VTX_LAST 0x02000000 #define A4XX_PC_PRIM_VTX_CNTL_PSIZE 0x04000000 -#define REG_A4XX_UNKNOWN_21C5 0x000021c5 +#define REG_A4XX_PC_PRIM_VTX_CNTL2 0x000021c5 +#define A4XX_PC_PRIM_VTX_CNTL2_POLYMODE_FRONT_PTYPE__MASK 0x00000007 +#define A4XX_PC_PRIM_VTX_CNTL2_POLYMODE_FRONT_PTYPE__SHIFT 0 +static inline uint32_t A4XX_PC_PRIM_VTX_CNTL2_POLYMODE_FRONT_PTYPE(enum adreno_pa_su_sc_draw val) +{ + return ((val) << A4XX_PC_PRIM_VTX_CNTL2_POLYMODE_FRONT_PTYPE__SHIFT) & A4XX_PC_PRIM_VTX_CNTL2_POLYMODE_FRONT_PTYPE__MASK; +} +#define A4XX_PC_PRIM_VTX_CNTL2_POLYMODE_BACK_PTYPE__MASK 0x00000038 +#define A4XX_PC_PRIM_VTX_CNTL2_POLYMODE_BACK_PTYPE__SHIFT 3 +static inline uint32_t A4XX_PC_PRIM_VTX_CNTL2_POLYMODE_BACK_PTYPE(enum adreno_pa_su_sc_draw val) +{ + return ((val) << A4XX_PC_PRIM_VTX_CNTL2_POLYMODE_BACK_PTYPE__SHIFT) & A4XX_PC_PRIM_VTX_CNTL2_POLYMODE_BACK_PTYPE__MASK; +} +#define A4XX_PC_PRIM_VTX_CNTL2_POLYMODE_ENABLE 0x00000040 #define REG_A4XX_PC_RESTART_INDEX 0x000021c6 @@ -2646,20 +3796,6 @@ static inline uint32_t A4XX_PC_HS_PARAM_PRIMTYPE(enum adreno_pa_su_sc_draw val) #define REG_A4XX_UNKNOWN_20EF 0x000020ef -#define REG_A4XX_UNKNOWN_20F0 0x000020f0 - -#define REG_A4XX_UNKNOWN_20F1 0x000020f1 - -#define REG_A4XX_UNKNOWN_20F2 0x000020f2 - -#define REG_A4XX_UNKNOWN_20F7 0x000020f7 -#define A4XX_UNKNOWN_20F7__MASK 0xffffffff -#define A4XX_UNKNOWN_20F7__SHIFT 0 -static inline uint32_t A4XX_UNKNOWN_20F7(float val) -{ - return ((fui(val)) << A4XX_UNKNOWN_20F7__SHIFT) & A4XX_UNKNOWN_20F7__MASK; -} - #define REG_A4XX_UNKNOWN_2152 0x00002152 #define REG_A4XX_UNKNOWN_2153 0x00002153 @@ -2720,6 +3856,12 @@ static inline uint32_t A4XX_TEX_SAMP_0_ANISO(enum a4xx_tex_aniso val) { return ((val) << A4XX_TEX_SAMP_0_ANISO__SHIFT) & A4XX_TEX_SAMP_0_ANISO__MASK; } +#define A4XX_TEX_SAMP_0_LOD_BIAS__MASK 0xfff80000 +#define A4XX_TEX_SAMP_0_LOD_BIAS__SHIFT 19 +static inline uint32_t A4XX_TEX_SAMP_0_LOD_BIAS(float val) +{ + return ((((int32_t)(val * 256.0))) << A4XX_TEX_SAMP_0_LOD_BIAS__SHIFT) & A4XX_TEX_SAMP_0_LOD_BIAS__MASK; +} #define REG_A4XX_TEX_SAMP_1 0x00000001 #define A4XX_TEX_SAMP_1_COMPARE_FUNC__MASK 0x0000000e @@ -2728,6 +3870,7 @@ static inline uint32_t A4XX_TEX_SAMP_1_COMPARE_FUNC(enum adreno_compare_func val { return ((val) << A4XX_TEX_SAMP_1_COMPARE_FUNC__SHIFT) & A4XX_TEX_SAMP_1_COMPARE_FUNC__MASK; } +#define A4XX_TEX_SAMP_1_CUBEMAPSEAMLESSFILTOFF 0x00000010 #define A4XX_TEX_SAMP_1_UNNORM_COORDS 0x00000020 #define A4XX_TEX_SAMP_1_MIPFILTER_LINEAR_FAR 0x00000040 #define A4XX_TEX_SAMP_1_MAX_LOD__MASK 0x000fff00 @@ -2796,7 +3939,7 @@ static inline uint32_t A4XX_TEX_CONST_1_HEIGHT(uint32_t val) { return ((val) << A4XX_TEX_CONST_1_HEIGHT__SHIFT) & A4XX_TEX_CONST_1_HEIGHT__MASK; } -#define A4XX_TEX_CONST_1_WIDTH__MASK 0x1fff8000 +#define A4XX_TEX_CONST_1_WIDTH__MASK 0x3fff8000 #define A4XX_TEX_CONST_1_WIDTH__SHIFT 15 static inline uint32_t A4XX_TEX_CONST_1_WIDTH(uint32_t val) { diff --git a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c index a53f1be05f75..d0d3c7baa8fe 100644 --- a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c @@ -102,11 +102,17 @@ static void a4xx_enable_hwcg(struct msm_gpu *gpu) gpu_write(gpu, REG_A4XX_RBBM_CLOCK_DELAY_TSE_RAS_RBBM, 0x00000222); gpu_write(gpu, REG_A4XX_RBBM_CLOCK_CTL_HLSQ , 0x00000000); gpu_write(gpu, REG_A4XX_RBBM_CLOCK_HYST_HLSQ, 0x00000000); - gpu_write(gpu, REG_A4XX_RBBM_CLOCK_DELAY_HLSQ, 0x00020000); - gpu_write(gpu, REG_A4XX_RBBM_CLOCK_CTL, 0xAAAAAAAA); + gpu_write(gpu, REG_A4XX_RBBM_CLOCK_DELAY_HLSQ, 0x00220000); + /* Early A430's have a timing issue with SP/TP power collapse; + disabling HW clock gating prevents it. */ + if (adreno_is_a430(adreno_gpu) && adreno_gpu->rev.patchid < 2) + gpu_write(gpu, REG_A4XX_RBBM_CLOCK_CTL, 0); + else + gpu_write(gpu, REG_A4XX_RBBM_CLOCK_CTL, 0xAAAAAAAA); gpu_write(gpu, REG_A4XX_RBBM_CLOCK_CTL2, 0); } + static void a4xx_me_init(struct msm_gpu *gpu) { struct msm_ringbuffer *ring = gpu->rb; @@ -141,7 +147,7 @@ static int a4xx_hw_init(struct msm_gpu *gpu) uint32_t *ptr, len; int i, ret; - if (adreno_is_a4xx(adreno_gpu)) { + if (adreno_is_a420(adreno_gpu)) { gpu_write(gpu, REG_A4XX_VBIF_ABIT_SORT, 0x0001001F); gpu_write(gpu, REG_A4XX_VBIF_ABIT_SORT_CONF, 0x000000A4); gpu_write(gpu, REG_A4XX_VBIF_GATE_OFF_WRREQ_EN, 0x00000001); @@ -150,6 +156,13 @@ static int a4xx_hw_init(struct msm_gpu *gpu) gpu_write(gpu, REG_A4XX_VBIF_IN_WR_LIM_CONF0, 0x18181818); gpu_write(gpu, REG_A4XX_VBIF_IN_WR_LIM_CONF1, 0x00000018); gpu_write(gpu, REG_A4XX_VBIF_ROUND_ROBIN_QOS_ARB, 0x00000003); + } else if (adreno_is_a430(adreno_gpu)) { + gpu_write(gpu, REG_A4XX_VBIF_GATE_OFF_WRREQ_EN, 0x00000001); + gpu_write(gpu, REG_A4XX_VBIF_IN_RD_LIM_CONF0, 0x18181818); + gpu_write(gpu, REG_A4XX_VBIF_IN_RD_LIM_CONF1, 0x00000018); + gpu_write(gpu, REG_A4XX_VBIF_IN_WR_LIM_CONF0, 0x18181818); + gpu_write(gpu, REG_A4XX_VBIF_IN_WR_LIM_CONF1, 0x00000018); + gpu_write(gpu, REG_A4XX_VBIF_ROUND_ROBIN_QOS_ARB, 0x00000003); } else { BUG(); } @@ -161,6 +174,10 @@ static int a4xx_hw_init(struct msm_gpu *gpu) gpu_write(gpu, REG_A4XX_RBBM_SP_HYST_CNT, 0x10); gpu_write(gpu, REG_A4XX_RBBM_WAIT_IDLE_CLOCKS_CTL, 0x10); + if (adreno_is_a430(adreno_gpu)) { + gpu_write(gpu, REG_A4XX_RBBM_WAIT_IDLE_CLOCKS_CTL2, 0x30); + } + /* Enable the RBBM error reporting bits */ gpu_write(gpu, REG_A4XX_RBBM_AHB_CTL0, 0x00000001); @@ -183,6 +200,14 @@ static int a4xx_hw_init(struct msm_gpu *gpu) /* Turn on performance counters: */ gpu_write(gpu, REG_A4XX_RBBM_PERFCTR_CTL, 0x01); + /* use the first CP counter for timestamp queries.. userspace may set + * this as well but it selects the same counter/countable: + */ + gpu_write(gpu, REG_A4XX_CP_PERFCTR_CP_SEL_0, CP_ALWAYS_COUNT); + + if (adreno_is_a430(adreno_gpu)) + gpu_write(gpu, REG_A4XX_UCHE_CACHE_WAYS_VFD, 0x07); + /* Disable L2 bypass to avoid UCHE out of bounds errors */ gpu_write(gpu, REG_A4XX_UCHE_TRAP_BASE_LO, 0xffff0000); gpu_write(gpu, REG_A4XX_UCHE_TRAP_BASE_HI, 0xffff0000); @@ -190,6 +215,15 @@ static int a4xx_hw_init(struct msm_gpu *gpu) gpu_write(gpu, REG_A4XX_CP_DEBUG, (1 << 25) | (adreno_is_a420(adreno_gpu) ? (1 << 29) : 0)); + /* On A430 enable SP regfile sleep for power savings */ + /* TODO downstream does this for !420, so maybe applies for 405 too? */ + if (!adreno_is_a420(adreno_gpu)) { + gpu_write(gpu, REG_A4XX_RBBM_SP_REGFILE_SLEEP_CNTL_0, + 0x00000441); + gpu_write(gpu, REG_A4XX_RBBM_SP_REGFILE_SLEEP_CNTL_1, + 0x00000441); + } + a4xx_enable_hwcg(gpu); /* @@ -204,10 +238,6 @@ static int a4xx_hw_init(struct msm_gpu *gpu) gpu_write(gpu, REG_A4XX_RBBM_CLOCK_DELAY_HLSQ, val); } - ret = adreno_hw_init(gpu); - if (ret) - return ret; - /* setup access protection: */ gpu_write(gpu, REG_A4XX_CP_PROTECT_CTRL, 0x00000007); @@ -263,6 +293,7 @@ static int a4xx_hw_init(struct msm_gpu *gpu) gpu_write(gpu, REG_A4XX_CP_ME_CNTL, 0); a4xx_me_init(gpu); + return 0; } @@ -317,6 +348,13 @@ static irqreturn_t a4xx_irq(struct msm_gpu *gpu) status = gpu_read(gpu, REG_A4XX_RBBM_INT_0_STATUS); DBG("%s: Int status %08x", gpu->name, status); + if (status & A4XX_INT0_CP_REG_PROTECT_FAULT) { + uint32_t reg = gpu_read(gpu, REG_A4XX_CP_PROTECT_STATUS); + printk("CP | Protected mode error| %s | addr=%x\n", + reg & (1 << 24) ? "WRITE" : "READ", + (reg & 0xFFFFF) >> 2); + } + gpu_write(gpu, REG_A4XX_RBBM_INT_CLEAR_CMD, status); msm_gpu_retire(gpu); @@ -512,12 +550,63 @@ static void a4xx_dump(struct msm_gpu *gpu) adreno_dump(gpu); } +static int a4xx_pm_resume(struct msm_gpu *gpu) { + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + int ret; + + ret = msm_gpu_pm_resume(gpu); + if (ret) + return ret; + + if (adreno_is_a430(adreno_gpu)) { + unsigned int reg; + /* Set the default register values; set SW_COLLAPSE to 0 */ + gpu_write(gpu, REG_A4XX_RBBM_POWER_CNTL_IP, 0x778000); + do { + udelay(5); + reg = gpu_read(gpu, REG_A4XX_RBBM_POWER_STATUS); + } while (!(reg & A4XX_RBBM_POWER_CNTL_IP_SP_TP_PWR_ON)); + } + return 0; +} + +static int a4xx_pm_suspend(struct msm_gpu *gpu) { + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + int ret; + + ret = msm_gpu_pm_suspend(gpu); + if (ret) + return ret; + + if (adreno_is_a430(adreno_gpu)) { + /* Set the default register values; set SW_COLLAPSE to 1 */ + gpu_write(gpu, REG_A4XX_RBBM_POWER_CNTL_IP, 0x778001); + } + return 0; +} + +static int a4xx_get_timestamp(struct msm_gpu *gpu, uint64_t *value) +{ + uint32_t hi, lo, tmp; + + tmp = gpu_read(gpu, REG_A4XX_RBBM_PERFCTR_CP_0_HI); + do { + hi = tmp; + lo = gpu_read(gpu, REG_A4XX_RBBM_PERFCTR_CP_0_LO); + tmp = gpu_read(gpu, REG_A4XX_RBBM_PERFCTR_CP_0_HI); + } while (tmp != hi); + + *value = (((uint64_t)hi) << 32) | lo; + + return 0; +} + static const struct adreno_gpu_funcs funcs = { .base = { .get_param = adreno_get_param, .hw_init = a4xx_hw_init, - .pm_suspend = msm_gpu_pm_suspend, - .pm_resume = msm_gpu_pm_resume, + .pm_suspend = a4xx_pm_suspend, + .pm_resume = a4xx_pm_resume, .recover = a4xx_recover, .last_fence = adreno_last_fence, .submit = adreno_submit, @@ -529,6 +618,7 @@ static const struct adreno_gpu_funcs funcs = { .show = a4xx_show, #endif }, + .get_timestamp = a4xx_get_timestamp, }; struct msm_gpu *a4xx_gpu_init(struct drm_device *dev) diff --git a/drivers/gpu/drm/msm/adreno/adreno_common.xml.h b/drivers/gpu/drm/msm/adreno/adreno_common.xml.h index c304468cf2bd..e81481d1b7df 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_common.xml.h +++ b/drivers/gpu/drm/msm/adreno/adreno_common.xml.h @@ -9,16 +9,17 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 398 bytes, from 2015-09-24 17:25:31) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) - /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10755 bytes, from 2015-09-14 20:46:55) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14968 bytes, from 2015-05-20 20:12:27) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 67771 bytes, from 2015-09-14 20:46:55) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 63970 bytes, from 2015-09-14 20:50:12) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 11518 bytes, from 2016-02-10 21:03:25) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 16166 bytes, from 2016-02-11 21:20:31) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 83967 bytes, from 2016-02-10 17:07:21) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 109916 bytes, from 2016-02-20 18:44:48) - /home/robclark/src/freedreno/envytools/rnndb/adreno/ocmem.xml ( 1773 bytes, from 2015-09-24 17:30:00) -Copyright (C) 2013-2015 by the following authors: +Copyright (C) 2013-2016 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) +- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -119,6 +120,23 @@ enum adreno_rb_copy_control_mode { RB_COPY_DEPTH_STENCIL = 5, }; +enum a3xx_rop_code { + ROP_CLEAR = 0, + ROP_NOR = 1, + ROP_AND_INVERTED = 2, + ROP_COPY_INVERTED = 3, + ROP_AND_REVERSE = 4, + ROP_INVERT = 5, + ROP_NAND = 7, + ROP_AND = 8, + ROP_EQUIV = 9, + ROP_NOOP = 10, + ROP_OR_INVERTED = 11, + ROP_OR_REVERSE = 13, + ROP_OR = 14, + ROP_SET = 15, +}; + enum a3xx_render_mode { RB_RENDERING_PASS = 0, RB_TILING_PASS = 1, diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c index 950d27d26b30..5127b75dbf40 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_device.c +++ b/drivers/gpu/drm/msm/adreno/adreno_device.c @@ -69,6 +69,14 @@ static const struct adreno_info gpulist[] = { .pfpfw = "a420_pfp.fw", .gmem = (SZ_1M + SZ_512K), .init = a4xx_gpu_init, + }, { + .rev = ADRENO_REV(4, 3, 0, ANY_ID), + .revn = 430, + .name = "A430", + .pm4fw = "a420_pm4.fw", + .pfpfw = "a420_pfp.fw", + .gmem = (SZ_1M + SZ_512K), + .init = a4xx_gpu_init, }, }; diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 0e1d0c57cd3d..4951172ede06 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -44,6 +44,10 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value) case MSM_PARAM_MAX_FREQ: *value = adreno_gpu->base.fast_rate; return 0; + case MSM_PARAM_TIMESTAMP: + if (adreno_gpu->funcs->get_timestamp) + return adreno_gpu->funcs->get_timestamp(gpu, value); + return -EINVAL; default: DBG("%s: invalid param: %u", gpu->name, param); return -EINVAL; @@ -71,18 +75,15 @@ int adreno_hw_init(struct msm_gpu *gpu) adreno_gpu_write(adreno_gpu, REG_ADRENO_CP_RB_CNTL, /* size is log2(quad-words): */ AXXX_CP_RB_CNTL_BUFSZ(ilog2(gpu->rb->size / 8)) | - AXXX_CP_RB_CNTL_BLKSZ(ilog2(RB_BLKSIZE / 8))); + AXXX_CP_RB_CNTL_BLKSZ(ilog2(RB_BLKSIZE / 8)) | + (adreno_is_a430(adreno_gpu) ? AXXX_CP_RB_CNTL_NO_UPDATE : 0)); /* Setup ringbuffer address: */ adreno_gpu_write(adreno_gpu, REG_ADRENO_CP_RB_BASE, gpu->rb_iova); - adreno_gpu_write(adreno_gpu, REG_ADRENO_CP_RB_RPTR_ADDR, - rbmemptr(adreno_gpu, rptr)); - - /* Setup scratch/timestamp: */ - adreno_gpu_write(adreno_gpu, REG_ADRENO_SCRATCH_ADDR, - rbmemptr(adreno_gpu, fence)); - adreno_gpu_write(adreno_gpu, REG_ADRENO_SCRATCH_UMSK, 0x1); + if (!adreno_is_a430(adreno_gpu)) + adreno_gpu_write(adreno_gpu, REG_ADRENO_CP_RB_RPTR_ADDR, + rbmemptr(adreno_gpu, rptr)); return 0; } @@ -92,6 +93,16 @@ static uint32_t get_wptr(struct msm_ringbuffer *ring) return ring->cur - ring->start; } +/* Use this helper to read rptr, since a430 doesn't update rptr in memory */ +static uint32_t get_rptr(struct adreno_gpu *adreno_gpu) +{ + if (adreno_is_a430(adreno_gpu)) + return adreno_gpu->memptrs->rptr = adreno_gpu_read( + adreno_gpu, REG_ADRENO_CP_RB_RPTR); + else + return adreno_gpu->memptrs->rptr; +} + uint32_t adreno_last_fence(struct msm_gpu *gpu) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); @@ -140,7 +151,8 @@ int adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, if (priv->lastctx == ctx) break; case MSM_SUBMIT_CMD_BUF: - OUT_PKT3(ring, CP_INDIRECT_BUFFER_PFD, 2); + OUT_PKT3(ring, adreno_is_a430(adreno_gpu) ? + CP_INDIRECT_BUFFER_PFE : CP_INDIRECT_BUFFER_PFD, 2); OUT_RING(ring, submit->cmd[i].iova); OUT_RING(ring, submit->cmd[i].size); ibs++; @@ -219,9 +231,12 @@ void adreno_idle(struct msm_gpu *gpu) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); uint32_t wptr = get_wptr(gpu->rb); + int ret; /* wait for CP to drain ringbuffer: */ - if (spin_until(adreno_gpu->memptrs->rptr == wptr)) + ret = spin_until(get_rptr(adreno_gpu) == wptr); + + if (ret) DRM_ERROR("%s: timeout waiting to drain ringbuffer!\n", gpu->name); /* TODO maybe we need to reset GPU here to recover from hang? */ @@ -240,7 +255,7 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m) seq_printf(m, "fence: %d/%d\n", adreno_gpu->memptrs->fence, gpu->submitted_fence); - seq_printf(m, "rptr: %d\n", adreno_gpu->memptrs->rptr); + seq_printf(m, "rptr: %d\n", get_rptr(adreno_gpu)); seq_printf(m, "wptr: %d\n", adreno_gpu->memptrs->wptr); seq_printf(m, "rb wptr: %d\n", get_wptr(gpu->rb)); @@ -281,7 +296,7 @@ void adreno_dump_info(struct msm_gpu *gpu) printk("fence: %d/%d\n", adreno_gpu->memptrs->fence, gpu->submitted_fence); - printk("rptr: %d\n", adreno_gpu->memptrs->rptr); + printk("rptr: %d\n", get_rptr(adreno_gpu)); printk("wptr: %d\n", adreno_gpu->memptrs->wptr); printk("rb wptr: %d\n", get_wptr(gpu->rb)); @@ -316,7 +331,7 @@ static uint32_t ring_freewords(struct msm_gpu *gpu) struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); uint32_t size = gpu->rb->size / 4; uint32_t wptr = get_wptr(gpu->rb); - uint32_t rptr = adreno_gpu->memptrs->rptr; + uint32_t rptr = get_rptr(adreno_gpu); return (rptr + (size - 1) - wptr) % size; } diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 0a312e9d3afd..1d07511f4d22 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -114,6 +114,7 @@ struct adreno_rev { struct adreno_gpu_funcs { struct msm_gpu_funcs base; + int (*get_timestamp)(struct msm_gpu *gpu, uint64_t *value); }; struct adreno_info { @@ -228,6 +229,11 @@ static inline int adreno_is_a420(struct adreno_gpu *gpu) return gpu->revn == 420; } +static inline int adreno_is_a430(struct adreno_gpu *gpu) +{ + return gpu->revn == 430; +} + int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value); int adreno_hw_init(struct msm_gpu *gpu); uint32_t adreno_last_fence(struct msm_gpu *gpu); diff --git a/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h b/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h index a22fef569499..d7477ff867c9 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h +++ b/drivers/gpu/drm/msm/adreno/adreno_pm4.xml.h @@ -9,16 +9,17 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/adreno.xml ( 398 bytes, from 2015-09-24 17:25:31) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) - /home/robclark/src/freedreno/envytools/rnndb/adreno/a2xx.xml ( 32901 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 10755 bytes, from 2015-09-14 20:46:55) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 14968 bytes, from 2015-05-20 20:12:27) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 67771 bytes, from 2015-09-14 20:46:55) -- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 63970 bytes, from 2015-09-14 20:50:12) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_common.xml ( 11518 bytes, from 2016-02-10 21:03:25) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/adreno_pm4.xml ( 16166 bytes, from 2016-02-11 21:20:31) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a3xx.xml ( 83967 bytes, from 2016-02-10 17:07:21) +- /home/robclark/src/freedreno/envytools/rnndb/adreno/a4xx.xml ( 109916 bytes, from 2016-02-20 18:44:48) - /home/robclark/src/freedreno/envytools/rnndb/adreno/ocmem.xml ( 1773 bytes, from 2015-09-24 17:30:00) -Copyright (C) 2013-2015 by the following authors: +Copyright (C) 2013-2016 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) +- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -172,6 +173,11 @@ enum adreno_pm4_type3_packets { CP_UNKNOWN_1A = 26, CP_UNKNOWN_4E = 78, CP_WIDE_REG_WRITE = 116, + CP_SCRATCH_TO_REG = 77, + CP_REG_TO_SCRATCH = 74, + CP_WAIT_MEM_WRITES = 18, + CP_COND_REG_EXEC = 71, + CP_MEM_TO_REG = 66, IN_IB_PREFETCH_END = 23, IN_SUBBLK_PREFETCH = 31, IN_INSTR_PREFETCH = 32, @@ -199,7 +205,11 @@ enum adreno_state_type { enum adreno_state_src { SS_DIRECT = 0, + SS_INVALID_ALL_IC = 2, + SS_INVALID_PART_IC = 3, SS_INDIRECT = 4, + SS_INDIRECT_TCM = 5, + SS_INDIRECT_STM = 6, }; enum a4xx_index_size { @@ -227,7 +237,7 @@ static inline uint32_t CP_LOAD_STATE_0_STATE_BLOCK(enum adreno_state_block val) { return ((val) << CP_LOAD_STATE_0_STATE_BLOCK__SHIFT) & CP_LOAD_STATE_0_STATE_BLOCK__MASK; } -#define CP_LOAD_STATE_0_NUM_UNIT__MASK 0x7fc00000 +#define CP_LOAD_STATE_0_NUM_UNIT__MASK 0xffc00000 #define CP_LOAD_STATE_0_NUM_UNIT__SHIFT 22 static inline uint32_t CP_LOAD_STATE_0_NUM_UNIT(uint32_t val) { @@ -499,5 +509,29 @@ static inline uint32_t CP_SET_BIN_DATA_1_BIN_SIZE_ADDRESS(uint32_t val) return ((val) << CP_SET_BIN_DATA_1_BIN_SIZE_ADDRESS__SHIFT) & CP_SET_BIN_DATA_1_BIN_SIZE_ADDRESS__MASK; } +#define REG_CP_REG_TO_MEM_0 0x00000000 +#define CP_REG_TO_MEM_0_REG__MASK 0x0000ffff +#define CP_REG_TO_MEM_0_REG__SHIFT 0 +static inline uint32_t CP_REG_TO_MEM_0_REG(uint32_t val) +{ + return ((val) << CP_REG_TO_MEM_0_REG__SHIFT) & CP_REG_TO_MEM_0_REG__MASK; +} +#define CP_REG_TO_MEM_0_CNT__MASK 0x3ff80000 +#define CP_REG_TO_MEM_0_CNT__SHIFT 19 +static inline uint32_t CP_REG_TO_MEM_0_CNT(uint32_t val) +{ + return ((val) << CP_REG_TO_MEM_0_CNT__SHIFT) & CP_REG_TO_MEM_0_CNT__MASK; +} +#define CP_REG_TO_MEM_0_64B 0x40000000 +#define CP_REG_TO_MEM_0_ACCUMULATE 0x80000000 + +#define REG_CP_REG_TO_MEM_1 0x00000001 +#define CP_REG_TO_MEM_1_DEST__MASK 0xffffffff +#define CP_REG_TO_MEM_1_DEST__SHIFT 0 +static inline uint32_t CP_REG_TO_MEM_1_DEST(uint32_t val) +{ + return ((val) << CP_REG_TO_MEM_1_DEST__SHIFT) & CP_REG_TO_MEM_1_DEST__MASK; +} + #endif /* ADRENO_PM4_XML */ diff --git a/drivers/gpu/drm/msm/dsi/dsi.xml.h b/drivers/gpu/drm/msm/dsi/dsi.xml.h index b2b5f3dd1b4c..4958594d5266 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.xml.h +++ b/drivers/gpu/drm/msm/dsi/dsi.xml.h @@ -9,7 +9,7 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 37194 bytes, from 2015-09-18 12:07:28) @@ -17,11 +17,12 @@ The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 41472 bytes, from 2016-01-22 18:18:18) - /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) +- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.c b/drivers/gpu/drm/msm/dsi/dsi_cfg.c index 2a827d8093a2..e58e9b91b34d 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_cfg.c +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.c @@ -57,10 +57,9 @@ static const char * const dsi_8916_bus_clk_names[] = { static const struct msm_dsi_config msm8916_dsi_cfg = { .io_offset = DSI_6G_REG_SHIFT, .reg_cfg = { - .num = 4, + .num = 3, .regs = { {"gdsc", -1, -1, -1, -1}, - {"vdd", 2850000, 2850000, 100000, 100}, {"vdda", 1200000, 1200000, 100000, 100}, {"vddio", 1800000, 1800000, 100000, 100}, }, diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 48f9967b4a1b..4282ec6bbaaf 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -163,6 +163,10 @@ struct msm_dsi_host { enum mipi_dsi_pixel_format format; unsigned long mode_flags; + /* lane data parsed via DT */ + int dlane_swap; + int num_data_lanes; + u32 dma_cmd_ctrl_restore; bool registered; @@ -845,19 +849,10 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, data = DSI_CTRL_CLK_EN; DBG("lane number=%d", msm_host->lanes); - if (msm_host->lanes == 2) { - data |= DSI_CTRL_LANE1 | DSI_CTRL_LANE2; - /* swap lanes for 2-lane panel for better performance */ - dsi_write(msm_host, REG_DSI_LANE_SWAP_CTRL, - DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL(LANE_SWAP_1230)); - } else { - /* Take 4 lanes as default */ - data |= DSI_CTRL_LANE0 | DSI_CTRL_LANE1 | DSI_CTRL_LANE2 | - DSI_CTRL_LANE3; - /* Do not swap lanes for 4-lane panel */ - dsi_write(msm_host, REG_DSI_LANE_SWAP_CTRL, - DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL(LANE_SWAP_0123)); - } + data |= ((DSI_CTRL_LANE0 << msm_host->lanes) - DSI_CTRL_LANE0); + + dsi_write(msm_host, REG_DSI_LANE_SWAP_CTRL, + DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL(msm_host->dlane_swap)); if (!(flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) dsi_write(msm_host, REG_DSI_LANE_CTRL, @@ -1479,13 +1474,14 @@ static int dsi_host_attach(struct mipi_dsi_host *host, struct msm_dsi_host *msm_host = to_msm_dsi_host(host); int ret; + if (dsi->lanes > msm_host->num_data_lanes) + return -EINVAL; + msm_host->channel = dsi->channel; msm_host->lanes = dsi->lanes; msm_host->format = dsi->format; msm_host->mode_flags = dsi->mode_flags; - WARN_ON(dsi->dev.of_node != msm_host->device_node); - /* Some gpios defined in panel DT need to be controlled by host */ ret = dsi_host_init_panel_gpios(msm_host, &dsi->dev); if (ret) @@ -1534,6 +1530,75 @@ static struct mipi_dsi_host_ops dsi_host_ops = { .transfer = dsi_host_transfer, }; +/* + * List of supported physical to logical lane mappings. + * For example, the 2nd entry represents the following mapping: + * + * "3012": Logic 3->Phys 0; Logic 0->Phys 1; Logic 1->Phys 2; Logic 2->Phys 3; + */ +static const int supported_data_lane_swaps[][4] = { + { 0, 1, 2, 3 }, + { 3, 0, 1, 2 }, + { 2, 3, 0, 1 }, + { 1, 2, 3, 0 }, + { 0, 3, 2, 1 }, + { 1, 0, 3, 2 }, + { 2, 1, 0, 3 }, + { 3, 2, 1, 0 }, +}; + +static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host, + struct device_node *ep) +{ + struct device *dev = &msm_host->pdev->dev; + struct property *prop; + u32 lane_map[4]; + int ret, i, len, num_lanes; + + prop = of_find_property(ep, "qcom,data-lane-map", &len); + if (!prop) { + dev_dbg(dev, "failed to find data lane mapping\n"); + return -EINVAL; + } + + num_lanes = len / sizeof(u32); + + if (num_lanes < 1 || num_lanes > 4) { + dev_err(dev, "bad number of data lanes\n"); + return -EINVAL; + } + + msm_host->num_data_lanes = num_lanes; + + ret = of_property_read_u32_array(ep, "qcom,data-lane-map", lane_map, + num_lanes); + if (ret) { + dev_err(dev, "failed to read lane data\n"); + return ret; + } + + /* + * compare DT specified physical-logical lane mappings with the ones + * supported by hardware + */ + for (i = 0; i < ARRAY_SIZE(supported_data_lane_swaps); i++) { + const int *swap = supported_data_lane_swaps[i]; + int j; + + for (j = 0; j < num_lanes; j++) { + if (swap[j] != lane_map[j]) + break; + } + + if (j == num_lanes) { + msm_host->dlane_swap = i; + return 0; + } + } + + return -EINVAL; +} + static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) { struct device *dev = &msm_host->pdev->dev; @@ -1560,17 +1625,21 @@ static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) return 0; } + ret = dsi_host_parse_lane_data(msm_host, endpoint); + if (ret) { + dev_err(dev, "%s: invalid lane configuration %d\n", + __func__, ret); + goto err; + } + /* Get panel node from the output port's endpoint data */ device_node = of_graph_get_remote_port_parent(endpoint); if (!device_node) { dev_err(dev, "%s: no valid device\n", __func__); - of_node_put(endpoint); - return -ENODEV; + ret = -ENODEV; + goto err; } - of_node_put(endpoint); - of_node_put(device_node); - msm_host->device_node = device_node; if (of_property_read_bool(np, "syscon-sfpb")) { @@ -1579,11 +1648,16 @@ static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) if (IS_ERR(msm_host->sfpb)) { dev_err(dev, "%s: failed to get sfpb regmap\n", __func__); - return PTR_ERR(msm_host->sfpb); + ret = PTR_ERR(msm_host->sfpb); } } - return 0; + of_node_put(device_node); + +err: + of_node_put(endpoint); + + return ret; } int msm_dsi_host_init(struct msm_dsi *msm_dsi) diff --git a/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h b/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h index 80ec65e47468..2d999494cdea 100644 --- a/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h +++ b/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h @@ -9,7 +9,7 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 37194 bytes, from 2015-09-18 12:07:28) @@ -17,11 +17,12 @@ The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 41472 bytes, from 2016-01-22 18:18:18) - /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) +- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h index 80b6038334a6..2cf1664723e8 100644 --- a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h @@ -97,8 +97,8 @@ static inline struct msm_dsi_pll *msm_dsi_pll_28nm_init( struct msm_dsi_pll *msm_dsi_pll_28nm_8960_init(struct platform_device *pdev, int id); #else -struct msm_dsi_pll *msm_dsi_pll_28nm_8960_init(struct platform_device *pdev, - int id) +static inline struct msm_dsi_pll *msm_dsi_pll_28nm_8960_init( + struct platform_device *pdev, int id) { return ERR_PTR(-ENODEV); } diff --git a/drivers/gpu/drm/msm/dsi/sfpb.xml.h b/drivers/gpu/drm/msm/dsi/sfpb.xml.h index 7d7662e69e11..506434fac993 100644 --- a/drivers/gpu/drm/msm/dsi/sfpb.xml.h +++ b/drivers/gpu/drm/msm/dsi/sfpb.xml.h @@ -9,7 +9,7 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 37194 bytes, from 2015-09-18 12:07:28) @@ -17,11 +17,12 @@ The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 41472 bytes, from 2016-01-22 18:18:18) - /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) +- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/drivers/gpu/drm/msm/edp/edp.xml.h b/drivers/gpu/drm/msm/edp/edp.xml.h index 90bf5ed46746..f1072c18c81e 100644 --- a/drivers/gpu/drm/msm/edp/edp.xml.h +++ b/drivers/gpu/drm/msm/edp/edp.xml.h @@ -9,7 +9,7 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 37194 bytes, from 2015-09-18 12:07:28) @@ -17,11 +17,12 @@ The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 41472 bytes, from 2016-01-22 18:18:18) - /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) +- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 9a0989c0b4de..51b9ea552f97 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -21,7 +21,7 @@ #include "hdmi.h" -void hdmi_set_mode(struct hdmi *hdmi, bool power_on) +void msm_hdmi_set_mode(struct hdmi *hdmi, bool power_on) { uint32_t ctrl = 0; unsigned long flags; @@ -46,29 +46,27 @@ void hdmi_set_mode(struct hdmi *hdmi, bool power_on) power_on ? "Enable" : "Disable", ctrl); } -static irqreturn_t hdmi_irq(int irq, void *dev_id) +static irqreturn_t msm_hdmi_irq(int irq, void *dev_id) { struct hdmi *hdmi = dev_id; /* Process HPD: */ - hdmi_connector_irq(hdmi->connector); + msm_hdmi_connector_irq(hdmi->connector); /* Process DDC: */ - hdmi_i2c_irq(hdmi->i2c); + msm_hdmi_i2c_irq(hdmi->i2c); /* Process HDCP: */ if (hdmi->hdcp_ctrl) - hdmi_hdcp_irq(hdmi->hdcp_ctrl); + msm_hdmi_hdcp_irq(hdmi->hdcp_ctrl); /* TODO audio.. */ return IRQ_HANDLED; } -static void hdmi_destroy(struct hdmi *hdmi) +static void msm_hdmi_destroy(struct hdmi *hdmi) { - struct hdmi_phy *phy = hdmi->phy; - /* * at this point, hpd has been disabled, * after flush workq, it's safe to deinit hdcp @@ -77,21 +75,53 @@ static void hdmi_destroy(struct hdmi *hdmi) flush_workqueue(hdmi->workq); destroy_workqueue(hdmi->workq); } - hdmi_hdcp_destroy(hdmi); - if (phy) - phy->funcs->destroy(phy); + msm_hdmi_hdcp_destroy(hdmi); + + if (hdmi->phy_dev) { + put_device(hdmi->phy_dev); + hdmi->phy = NULL; + hdmi->phy_dev = NULL; + } if (hdmi->i2c) - hdmi_i2c_destroy(hdmi->i2c); + msm_hdmi_i2c_destroy(hdmi->i2c); platform_set_drvdata(hdmi->pdev, NULL); } +static int msm_hdmi_get_phy(struct hdmi *hdmi) +{ + struct platform_device *pdev = hdmi->pdev; + struct platform_device *phy_pdev; + struct device_node *phy_node; + + phy_node = of_parse_phandle(pdev->dev.of_node, "phys", 0); + if (!phy_node) { + dev_err(&pdev->dev, "cannot find phy device\n"); + return -ENXIO; + } + + phy_pdev = of_find_device_by_node(phy_node); + if (phy_pdev) + hdmi->phy = platform_get_drvdata(phy_pdev); + + of_node_put(phy_node); + + if (!phy_pdev || !hdmi->phy) { + dev_err(&pdev->dev, "phy driver is not ready\n"); + return -EPROBE_DEFER; + } + + hdmi->phy_dev = get_device(&phy_pdev->dev); + + return 0; +} + /* construct hdmi at bind/probe time, grab all the resources. If * we are to EPROBE_DEFER we want to do it here, rather than later * at modeset_init() time */ -static struct hdmi *hdmi_init(struct platform_device *pdev) +static struct hdmi *msm_hdmi_init(struct platform_device *pdev) { struct hdmi_platform_config *config = pdev->dev.platform_data; struct hdmi *hdmi = NULL; @@ -108,18 +138,6 @@ static struct hdmi *hdmi_init(struct platform_device *pdev) hdmi->config = config; spin_lock_init(&hdmi->reg_lock); - /* not sure about which phy maps to which msm.. probably I miss some */ - if (config->phy_init) { - hdmi->phy = config->phy_init(hdmi); - - if (IS_ERR(hdmi->phy)) { - ret = PTR_ERR(hdmi->phy); - dev_err(&pdev->dev, "failed to load phy: %d\n", ret); - hdmi->phy = NULL; - goto fail; - } - } - hdmi->mmio = msm_ioremap(pdev, config->mmio_name, "HDMI"); if (IS_ERR(hdmi->mmio)) { ret = PTR_ERR(hdmi->mmio); @@ -222,7 +240,7 @@ static struct hdmi *hdmi_init(struct platform_device *pdev) hdmi->workq = alloc_ordered_workqueue("msm_hdmi", 0); - hdmi->i2c = hdmi_i2c_init(hdmi); + hdmi->i2c = msm_hdmi_i2c_init(hdmi); if (IS_ERR(hdmi->i2c)) { ret = PTR_ERR(hdmi->i2c); dev_err(&pdev->dev, "failed to get i2c: %d\n", ret); @@ -230,7 +248,13 @@ static struct hdmi *hdmi_init(struct platform_device *pdev) goto fail; } - hdmi->hdcp_ctrl = hdmi_hdcp_init(hdmi); + ret = msm_hdmi_get_phy(hdmi); + if (ret) { + dev_err(&pdev->dev, "failed to get phy\n"); + goto fail; + } + + hdmi->hdcp_ctrl = msm_hdmi_hdcp_init(hdmi); if (IS_ERR(hdmi->hdcp_ctrl)) { dev_warn(&pdev->dev, "failed to init hdcp: disabled\n"); hdmi->hdcp_ctrl = NULL; @@ -240,7 +264,7 @@ static struct hdmi *hdmi_init(struct platform_device *pdev) fail: if (hdmi) - hdmi_destroy(hdmi); + msm_hdmi_destroy(hdmi); return ERR_PTR(ret); } @@ -250,10 +274,10 @@ fail: * driver (not hdmi sub-device's probe/bind!) * * Any resource (regulator/clk/etc) which could be missing at boot - * should be handled in hdmi_init() so that failure happens from + * should be handled in msm_hdmi_init() so that failure happens from * hdmi sub-device's probe. */ -int hdmi_modeset_init(struct hdmi *hdmi, +int msm_hdmi_modeset_init(struct hdmi *hdmi, struct drm_device *dev, struct drm_encoder *encoder) { struct msm_drm_private *priv = dev->dev_private; @@ -265,7 +289,7 @@ int hdmi_modeset_init(struct hdmi *hdmi, hdmi_audio_infoframe_init(&hdmi->audio.infoframe); - hdmi->bridge = hdmi_bridge_init(hdmi); + hdmi->bridge = msm_hdmi_bridge_init(hdmi); if (IS_ERR(hdmi->bridge)) { ret = PTR_ERR(hdmi->bridge); dev_err(dev->dev, "failed to create HDMI bridge: %d\n", ret); @@ -273,7 +297,7 @@ int hdmi_modeset_init(struct hdmi *hdmi, goto fail; } - hdmi->connector = hdmi_connector_init(hdmi); + hdmi->connector = msm_hdmi_connector_init(hdmi); if (IS_ERR(hdmi->connector)) { ret = PTR_ERR(hdmi->connector); dev_err(dev->dev, "failed to create HDMI connector: %d\n", ret); @@ -289,7 +313,7 @@ int hdmi_modeset_init(struct hdmi *hdmi, } ret = devm_request_irq(&pdev->dev, hdmi->irq, - hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + msm_hdmi_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "hdmi_isr", hdmi); if (ret < 0) { dev_err(dev->dev, "failed to request IRQ%u: %d\n", @@ -309,7 +333,7 @@ int hdmi_modeset_init(struct hdmi *hdmi, fail: /* bridge is normally destroyed by drm: */ if (hdmi->bridge) { - hdmi_bridge_destroy(hdmi->bridge); + msm_hdmi_bridge_destroy(hdmi->bridge); hdmi->bridge = NULL; } if (hdmi->connector) { @@ -331,15 +355,12 @@ fail: static const char *pwr_reg_names_none[] = {}; static const char *hpd_reg_names_none[] = {}; -static struct hdmi_platform_config hdmi_tx_8660_config = { - .phy_init = hdmi_phy_8x60_init, -}; +static struct hdmi_platform_config hdmi_tx_8660_config; static const char *hpd_reg_names_8960[] = {"core-vdda", "hdmi-mux"}; static const char *hpd_clk_names_8960[] = {"core_clk", "master_iface_clk", "slave_iface_clk"}; static struct hdmi_platform_config hdmi_tx_8960_config = { - .phy_init = hdmi_phy_8960_init, HDMI_CFG(hpd_reg, 8960), HDMI_CFG(hpd_clk, 8960), }; @@ -351,7 +372,6 @@ static const char *hpd_clk_names_8x74[] = {"iface_clk", "core_clk", "mdp_core_cl static unsigned long hpd_clk_freq_8x74[] = {0, 19200000, 0}; static struct hdmi_platform_config hdmi_tx_8974_config = { - .phy_init = hdmi_phy_8x74_init, HDMI_CFG(pwr_reg, 8x74), HDMI_CFG(hpd_reg, 8x74), HDMI_CFG(pwr_clk, 8x74), @@ -362,7 +382,6 @@ static struct hdmi_platform_config hdmi_tx_8974_config = { static const char *hpd_reg_names_8084[] = {"hpd-gdsc", "hpd-5v", "hpd-5v-en"}; static struct hdmi_platform_config hdmi_tx_8084_config = { - .phy_init = hdmi_phy_8x74_init, HDMI_CFG(pwr_reg, 8x74), HDMI_CFG(hpd_reg, 8084), HDMI_CFG(pwr_clk, 8x74), @@ -371,7 +390,6 @@ static struct hdmi_platform_config hdmi_tx_8084_config = { }; static struct hdmi_platform_config hdmi_tx_8994_config = { - .phy_init = NULL, /* nothing to do for this HDMI PHY 20nm */ HDMI_CFG(pwr_reg, 8x74), HDMI_CFG(hpd_reg, none), HDMI_CFG(pwr_clk, 8x74), @@ -380,7 +398,6 @@ static struct hdmi_platform_config hdmi_tx_8994_config = { }; static struct hdmi_platform_config hdmi_tx_8996_config = { - .phy_init = NULL, HDMI_CFG(pwr_reg, none), HDMI_CFG(hpd_reg, none), HDMI_CFG(pwr_clk, 8x74), @@ -388,7 +405,21 @@ static struct hdmi_platform_config hdmi_tx_8996_config = { .hpd_freq = hpd_clk_freq_8x74, }; -static int get_gpio(struct device *dev, struct device_node *of_node, const char *name) +static const struct { + const char *name; + const bool output; + const int value; + const char *label; +} msm_hdmi_gpio_pdata[] = { + { "qcom,hdmi-tx-ddc-clk", true, 1, "HDMI_DDC_CLK" }, + { "qcom,hdmi-tx-ddc-data", true, 1, "HDMI_DDC_DATA" }, + { "qcom,hdmi-tx-hpd", false, 1, "HDMI_HPD" }, + { "qcom,hdmi-tx-mux-en", true, 1, "HDMI_MUX_EN" }, + { "qcom,hdmi-tx-mux-sel", true, 0, "HDMI_MUX_SEL" }, + { "qcom,hdmi-tx-mux-lpm", true, 1, "HDMI_MUX_LPM" }, +}; + +static int msm_hdmi_get_gpio(struct device_node *of_node, const char *name) { int gpio = of_get_named_gpio(of_node, name, 0); if (gpio < 0) { @@ -403,13 +434,14 @@ static int get_gpio(struct device *dev, struct device_node *of_node, const char return gpio; } -static int hdmi_bind(struct device *dev, struct device *master, void *data) +static int msm_hdmi_bind(struct device *dev, struct device *master, void *data) { struct drm_device *drm = dev_get_drvdata(master); struct msm_drm_private *priv = drm->dev_private; static struct hdmi_platform_config *hdmi_cfg; struct hdmi *hdmi; struct device_node *of_node = dev->of_node; + int i; hdmi_cfg = (struct hdmi_platform_config *) of_device_get_match_data(dev); @@ -420,16 +452,18 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) hdmi_cfg->mmio_name = "core_physical"; hdmi_cfg->qfprom_mmio_name = "qfprom_physical"; - hdmi_cfg->ddc_clk_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-clk"); - hdmi_cfg->ddc_data_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-ddc-data"); - hdmi_cfg->hpd_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-hpd"); - hdmi_cfg->mux_en_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-mux-en"); - hdmi_cfg->mux_sel_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-mux-sel"); - hdmi_cfg->mux_lpm_gpio = get_gpio(dev, of_node, "qcom,hdmi-tx-mux-lpm"); + + for (i = 0; i < HDMI_MAX_NUM_GPIO; i++) { + hdmi_cfg->gpios[i].num = msm_hdmi_get_gpio(of_node, + msm_hdmi_gpio_pdata[i].name); + hdmi_cfg->gpios[i].output = msm_hdmi_gpio_pdata[i].output; + hdmi_cfg->gpios[i].value = msm_hdmi_gpio_pdata[i].value; + hdmi_cfg->gpios[i].label = msm_hdmi_gpio_pdata[i].label; + } dev->platform_data = hdmi_cfg; - hdmi = hdmi_init(to_platform_device(dev)); + hdmi = msm_hdmi_init(to_platform_device(dev)); if (IS_ERR(hdmi)) return PTR_ERR(hdmi); priv->hdmi = hdmi; @@ -437,34 +471,34 @@ static int hdmi_bind(struct device *dev, struct device *master, void *data) return 0; } -static void hdmi_unbind(struct device *dev, struct device *master, +static void msm_hdmi_unbind(struct device *dev, struct device *master, void *data) { struct drm_device *drm = dev_get_drvdata(master); struct msm_drm_private *priv = drm->dev_private; if (priv->hdmi) { - hdmi_destroy(priv->hdmi); + msm_hdmi_destroy(priv->hdmi); priv->hdmi = NULL; } } -static const struct component_ops hdmi_ops = { - .bind = hdmi_bind, - .unbind = hdmi_unbind, +static const struct component_ops msm_hdmi_ops = { + .bind = msm_hdmi_bind, + .unbind = msm_hdmi_unbind, }; -static int hdmi_dev_probe(struct platform_device *pdev) +static int msm_hdmi_dev_probe(struct platform_device *pdev) { - return component_add(&pdev->dev, &hdmi_ops); + return component_add(&pdev->dev, &msm_hdmi_ops); } -static int hdmi_dev_remove(struct platform_device *pdev) +static int msm_hdmi_dev_remove(struct platform_device *pdev) { - component_del(&pdev->dev, &hdmi_ops); + component_del(&pdev->dev, &msm_hdmi_ops); return 0; } -static const struct of_device_id dt_match[] = { +static const struct of_device_id msm_hdmi_dt_match[] = { { .compatible = "qcom,hdmi-tx-8996", .data = &hdmi_tx_8996_config }, { .compatible = "qcom,hdmi-tx-8994", .data = &hdmi_tx_8994_config }, { .compatible = "qcom,hdmi-tx-8084", .data = &hdmi_tx_8084_config }, @@ -474,21 +508,23 @@ static const struct of_device_id dt_match[] = { {} }; -static struct platform_driver hdmi_driver = { - .probe = hdmi_dev_probe, - .remove = hdmi_dev_remove, +static struct platform_driver msm_hdmi_driver = { + .probe = msm_hdmi_dev_probe, + .remove = msm_hdmi_dev_remove, .driver = { .name = "hdmi_msm", - .of_match_table = dt_match, + .of_match_table = msm_hdmi_dt_match, }, }; -void __init hdmi_register(void) +void __init msm_hdmi_register(void) { - platform_driver_register(&hdmi_driver); + msm_hdmi_phy_driver_register(); + platform_driver_register(&msm_hdmi_driver); } -void __exit hdmi_unregister(void) +void __exit msm_hdmi_unregister(void) { - platform_driver_unregister(&hdmi_driver); + platform_driver_unregister(&msm_hdmi_driver); + msm_hdmi_phy_driver_unregister(); } diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index d0e663192d01..b04a64664673 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -27,10 +27,18 @@ #include "msm_drv.h" #include "hdmi.xml.h" +#define HDMI_MAX_NUM_GPIO 6 struct hdmi_phy; struct hdmi_platform_config; +struct hdmi_gpio_data { + int num; + bool output; + int value; + const char *label; +}; + struct hdmi_audio { bool enabled; struct hdmi_audio_infoframe infoframe; @@ -62,6 +70,8 @@ struct hdmi { struct clk **pwr_clks; struct hdmi_phy *phy; + struct device *phy_dev; + struct i2c_adapter *i2c; struct drm_connector *connector; struct drm_bridge *bridge; @@ -88,7 +98,6 @@ struct hdmi { /* platform config data (ie. from DT, or pdata) */ struct hdmi_platform_config { - struct hdmi_phy *(*phy_init)(struct hdmi *hdmi); const char *mmio_name; const char *qfprom_mmio_name; @@ -110,11 +119,10 @@ struct hdmi_platform_config { int pwr_clk_cnt; /* gpio's: */ - int ddc_clk_gpio, ddc_data_gpio, hpd_gpio, mux_en_gpio, mux_sel_gpio; - int mux_lpm_gpio; + struct hdmi_gpio_data gpios[HDMI_MAX_NUM_GPIO]; }; -void hdmi_set_mode(struct hdmi *hdmi, bool power_on); +void msm_hdmi_set_mode(struct hdmi *hdmi, bool power_on); static inline void hdmi_write(struct hdmi *hdmi, u32 reg, u32 data) { @@ -132,65 +140,113 @@ static inline u32 hdmi_qfprom_read(struct hdmi *hdmi, u32 reg) } /* - * The phy appears to be different, for example between 8960 and 8x60, - * so split the phy related functions out and load the correct one at - * runtime: + * hdmi phy: */ -struct hdmi_phy_funcs { - void (*destroy)(struct hdmi_phy *phy); +enum hdmi_phy_type { + MSM_HDMI_PHY_8x60, + MSM_HDMI_PHY_8960, + MSM_HDMI_PHY_8x74, + MSM_HDMI_PHY_8996, + MSM_HDMI_PHY_MAX, +}; + +struct hdmi_phy_cfg { + enum hdmi_phy_type type; void (*powerup)(struct hdmi_phy *phy, unsigned long int pixclock); void (*powerdown)(struct hdmi_phy *phy); + const char * const *reg_names; + int num_regs; + const char * const *clk_names; + int num_clks; }; +extern const struct hdmi_phy_cfg msm_hdmi_phy_8x60_cfg; +extern const struct hdmi_phy_cfg msm_hdmi_phy_8960_cfg; +extern const struct hdmi_phy_cfg msm_hdmi_phy_8x74_cfg; +extern const struct hdmi_phy_cfg msm_hdmi_phy_8996_cfg; + struct hdmi_phy { + struct platform_device *pdev; + void __iomem *mmio; + struct hdmi_phy_cfg *cfg; const struct hdmi_phy_funcs *funcs; + struct regulator **regs; + struct clk **clks; }; -struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi); -struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi); -struct hdmi_phy *hdmi_phy_8x74_init(struct hdmi *hdmi); +static inline void hdmi_phy_write(struct hdmi_phy *phy, u32 reg, u32 data) +{ + msm_writel(data, phy->mmio + reg); +} + +static inline u32 hdmi_phy_read(struct hdmi_phy *phy, u32 reg) +{ + return msm_readl(phy->mmio + reg); +} + +int msm_hdmi_phy_resource_enable(struct hdmi_phy *phy); +void msm_hdmi_phy_resource_disable(struct hdmi_phy *phy); +void msm_hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long int pixclock); +void msm_hdmi_phy_powerdown(struct hdmi_phy *phy); +void __init msm_hdmi_phy_driver_register(void); +void __exit msm_hdmi_phy_driver_unregister(void); + +#ifdef CONFIG_COMMON_CLK +int msm_hdmi_pll_8960_init(struct platform_device *pdev); +int msm_hdmi_pll_8996_init(struct platform_device *pdev); +#else +static inline int msm_hdmi_pll_8960_init(struct platform_device *pdev); +{ + return -ENODEV; +} + +static inline int msm_hdmi_pll_8996_init(struct platform_device *pdev) +{ + return -ENODEV; +} +#endif /* * audio: */ -int hdmi_audio_update(struct hdmi *hdmi); -int hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled, +int msm_hdmi_audio_update(struct hdmi *hdmi); +int msm_hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled, uint32_t num_of_channels, uint32_t channel_allocation, uint32_t level_shift, bool down_mix); -void hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate); +void msm_hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate); /* * hdmi bridge: */ -struct drm_bridge *hdmi_bridge_init(struct hdmi *hdmi); -void hdmi_bridge_destroy(struct drm_bridge *bridge); +struct drm_bridge *msm_hdmi_bridge_init(struct hdmi *hdmi); +void msm_hdmi_bridge_destroy(struct drm_bridge *bridge); /* * hdmi connector: */ -void hdmi_connector_irq(struct drm_connector *connector); -struct drm_connector *hdmi_connector_init(struct hdmi *hdmi); +void msm_hdmi_connector_irq(struct drm_connector *connector); +struct drm_connector *msm_hdmi_connector_init(struct hdmi *hdmi); /* * i2c adapter for ddc: */ -void hdmi_i2c_irq(struct i2c_adapter *i2c); -void hdmi_i2c_destroy(struct i2c_adapter *i2c); -struct i2c_adapter *hdmi_i2c_init(struct hdmi *hdmi); +void msm_hdmi_i2c_irq(struct i2c_adapter *i2c); +void msm_hdmi_i2c_destroy(struct i2c_adapter *i2c); +struct i2c_adapter *msm_hdmi_i2c_init(struct hdmi *hdmi); /* * hdcp */ -struct hdmi_hdcp_ctrl *hdmi_hdcp_init(struct hdmi *hdmi); -void hdmi_hdcp_destroy(struct hdmi *hdmi); -void hdmi_hdcp_on(struct hdmi_hdcp_ctrl *hdcp_ctrl); -void hdmi_hdcp_off(struct hdmi_hdcp_ctrl *hdcp_ctrl); -void hdmi_hdcp_irq(struct hdmi_hdcp_ctrl *hdcp_ctrl); +struct hdmi_hdcp_ctrl *msm_hdmi_hdcp_init(struct hdmi *hdmi); +void msm_hdmi_hdcp_destroy(struct hdmi *hdmi); +void msm_hdmi_hdcp_on(struct hdmi_hdcp_ctrl *hdcp_ctrl); +void msm_hdmi_hdcp_off(struct hdmi_hdcp_ctrl *hdcp_ctrl); +void msm_hdmi_hdcp_irq(struct hdmi_hdcp_ctrl *hdcp_ctrl); #endif /* __HDMI_CONNECTOR_H__ */ diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h index 10c45700aefe..34c7df6549c1 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.xml.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.xml.h @@ -9,7 +9,7 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 37194 bytes, from 2015-09-18 12:07:28) @@ -17,11 +17,12 @@ The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 41472 bytes, from 2016-01-22 18:18:18) - /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) -Copyright (C) 2013-2015 by the following authors: +Copyright (C) 2013-2016 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) +- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the @@ -559,7 +560,7 @@ static inline uint32_t HDMI_VSYNC_TOTAL_F2_V_TOTAL(uint32_t val) #define REG_HDMI_CEC_WR_CHECK_CONFIG 0x00000370 -#define REG_HDMI_8x60_PHY_REG0 0x00000300 +#define REG_HDMI_8x60_PHY_REG0 0x00000000 #define HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__MASK 0x0000001c #define HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__SHIFT 2 static inline uint32_t HDMI_8x60_PHY_REG0_DESER_DEL_CTRL(uint32_t val) @@ -567,7 +568,7 @@ static inline uint32_t HDMI_8x60_PHY_REG0_DESER_DEL_CTRL(uint32_t val) return ((val) << HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__SHIFT) & HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__MASK; } -#define REG_HDMI_8x60_PHY_REG1 0x00000304 +#define REG_HDMI_8x60_PHY_REG1 0x00000004 #define HDMI_8x60_PHY_REG1_DTEST_MUX_SEL__MASK 0x000000f0 #define HDMI_8x60_PHY_REG1_DTEST_MUX_SEL__SHIFT 4 static inline uint32_t HDMI_8x60_PHY_REG1_DTEST_MUX_SEL(uint32_t val) @@ -581,7 +582,7 @@ static inline uint32_t HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(uint32_t val) return ((val) << HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL__SHIFT) & HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL__MASK; } -#define REG_HDMI_8x60_PHY_REG2 0x00000308 +#define REG_HDMI_8x60_PHY_REG2 0x00000008 #define HDMI_8x60_PHY_REG2_PD_DESER 0x00000001 #define HDMI_8x60_PHY_REG2_PD_DRIVE_1 0x00000002 #define HDMI_8x60_PHY_REG2_PD_DRIVE_2 0x00000004 @@ -591,152 +592,152 @@ static inline uint32_t HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(uint32_t val) #define HDMI_8x60_PHY_REG2_PD_PWRGEN 0x00000040 #define HDMI_8x60_PHY_REG2_RCV_SENSE_EN 0x00000080 -#define REG_HDMI_8x60_PHY_REG3 0x0000030c +#define REG_HDMI_8x60_PHY_REG3 0x0000000c #define HDMI_8x60_PHY_REG3_PLL_ENABLE 0x00000001 -#define REG_HDMI_8x60_PHY_REG4 0x00000310 +#define REG_HDMI_8x60_PHY_REG4 0x00000010 -#define REG_HDMI_8x60_PHY_REG5 0x00000314 +#define REG_HDMI_8x60_PHY_REG5 0x00000014 -#define REG_HDMI_8x60_PHY_REG6 0x00000318 +#define REG_HDMI_8x60_PHY_REG6 0x00000018 -#define REG_HDMI_8x60_PHY_REG7 0x0000031c +#define REG_HDMI_8x60_PHY_REG7 0x0000001c -#define REG_HDMI_8x60_PHY_REG8 0x00000320 +#define REG_HDMI_8x60_PHY_REG8 0x00000020 -#define REG_HDMI_8x60_PHY_REG9 0x00000324 +#define REG_HDMI_8x60_PHY_REG9 0x00000024 -#define REG_HDMI_8x60_PHY_REG10 0x00000328 +#define REG_HDMI_8x60_PHY_REG10 0x00000028 -#define REG_HDMI_8x60_PHY_REG11 0x0000032c +#define REG_HDMI_8x60_PHY_REG11 0x0000002c -#define REG_HDMI_8x60_PHY_REG12 0x00000330 +#define REG_HDMI_8x60_PHY_REG12 0x00000030 #define HDMI_8x60_PHY_REG12_RETIMING_EN 0x00000001 #define HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN 0x00000002 #define HDMI_8x60_PHY_REG12_FORCE_LOCK 0x00000010 -#define REG_HDMI_8960_PHY_REG0 0x00000400 +#define REG_HDMI_8960_PHY_REG0 0x00000000 -#define REG_HDMI_8960_PHY_REG1 0x00000404 +#define REG_HDMI_8960_PHY_REG1 0x00000004 -#define REG_HDMI_8960_PHY_REG2 0x00000408 +#define REG_HDMI_8960_PHY_REG2 0x00000008 -#define REG_HDMI_8960_PHY_REG3 0x0000040c +#define REG_HDMI_8960_PHY_REG3 0x0000000c -#define REG_HDMI_8960_PHY_REG4 0x00000410 +#define REG_HDMI_8960_PHY_REG4 0x00000010 -#define REG_HDMI_8960_PHY_REG5 0x00000414 +#define REG_HDMI_8960_PHY_REG5 0x00000014 -#define REG_HDMI_8960_PHY_REG6 0x00000418 +#define REG_HDMI_8960_PHY_REG6 0x00000018 -#define REG_HDMI_8960_PHY_REG7 0x0000041c +#define REG_HDMI_8960_PHY_REG7 0x0000001c -#define REG_HDMI_8960_PHY_REG8 0x00000420 +#define REG_HDMI_8960_PHY_REG8 0x00000020 -#define REG_HDMI_8960_PHY_REG9 0x00000424 +#define REG_HDMI_8960_PHY_REG9 0x00000024 -#define REG_HDMI_8960_PHY_REG10 0x00000428 +#define REG_HDMI_8960_PHY_REG10 0x00000028 -#define REG_HDMI_8960_PHY_REG11 0x0000042c +#define REG_HDMI_8960_PHY_REG11 0x0000002c -#define REG_HDMI_8960_PHY_REG12 0x00000430 +#define REG_HDMI_8960_PHY_REG12 0x00000030 #define HDMI_8960_PHY_REG12_SW_RESET 0x00000020 #define HDMI_8960_PHY_REG12_PWRDN_B 0x00000080 -#define REG_HDMI_8960_PHY_REG_BIST_CFG 0x00000434 +#define REG_HDMI_8960_PHY_REG_BIST_CFG 0x00000034 -#define REG_HDMI_8960_PHY_DEBUG_BUS_SEL 0x00000438 +#define REG_HDMI_8960_PHY_DEBUG_BUS_SEL 0x00000038 -#define REG_HDMI_8960_PHY_REG_MISC0 0x0000043c +#define REG_HDMI_8960_PHY_REG_MISC0 0x0000003c -#define REG_HDMI_8960_PHY_REG13 0x00000440 +#define REG_HDMI_8960_PHY_REG13 0x00000040 -#define REG_HDMI_8960_PHY_REG14 0x00000444 +#define REG_HDMI_8960_PHY_REG14 0x00000044 -#define REG_HDMI_8960_PHY_REG15 0x00000448 +#define REG_HDMI_8960_PHY_REG15 0x00000048 -#define REG_HDMI_8960_PHY_PLL_REFCLK_CFG 0x00000500 +#define REG_HDMI_8960_PHY_PLL_REFCLK_CFG 0x00000000 -#define REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG 0x00000504 +#define REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG 0x00000004 -#define REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 0x00000508 +#define REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 0x00000008 -#define REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 0x0000050c +#define REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 0x0000000c -#define REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG 0x00000510 +#define REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG 0x00000010 -#define REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG 0x00000514 +#define REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG 0x00000014 -#define REG_HDMI_8960_PHY_PLL_PWRDN_B 0x00000518 +#define REG_HDMI_8960_PHY_PLL_PWRDN_B 0x00000018 #define HDMI_8960_PHY_PLL_PWRDN_B_PD_PLL 0x00000002 #define HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B 0x00000008 -#define REG_HDMI_8960_PHY_PLL_SDM_CFG0 0x0000051c +#define REG_HDMI_8960_PHY_PLL_SDM_CFG0 0x0000001c -#define REG_HDMI_8960_PHY_PLL_SDM_CFG1 0x00000520 +#define REG_HDMI_8960_PHY_PLL_SDM_CFG1 0x00000020 -#define REG_HDMI_8960_PHY_PLL_SDM_CFG2 0x00000524 +#define REG_HDMI_8960_PHY_PLL_SDM_CFG2 0x00000024 -#define REG_HDMI_8960_PHY_PLL_SDM_CFG3 0x00000528 +#define REG_HDMI_8960_PHY_PLL_SDM_CFG3 0x00000028 -#define REG_HDMI_8960_PHY_PLL_SDM_CFG4 0x0000052c +#define REG_HDMI_8960_PHY_PLL_SDM_CFG4 0x0000002c -#define REG_HDMI_8960_PHY_PLL_SSC_CFG0 0x00000530 +#define REG_HDMI_8960_PHY_PLL_SSC_CFG0 0x00000030 -#define REG_HDMI_8960_PHY_PLL_SSC_CFG1 0x00000534 +#define REG_HDMI_8960_PHY_PLL_SSC_CFG1 0x00000034 -#define REG_HDMI_8960_PHY_PLL_SSC_CFG2 0x00000538 +#define REG_HDMI_8960_PHY_PLL_SSC_CFG2 0x00000038 -#define REG_HDMI_8960_PHY_PLL_SSC_CFG3 0x0000053c +#define REG_HDMI_8960_PHY_PLL_SSC_CFG3 0x0000003c -#define REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 0x00000540 +#define REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 0x00000040 -#define REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 0x00000544 +#define REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 0x00000044 -#define REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 0x00000548 +#define REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 0x00000048 -#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 0x0000054c +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 0x0000004c -#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 0x00000550 +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 0x00000050 -#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 0x00000554 +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 0x00000054 -#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 0x00000558 +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 0x00000058 -#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 0x0000055c +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 0x0000005c -#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 0x00000560 +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 0x00000060 -#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 0x00000564 +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 0x00000064 -#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 0x00000568 +#define REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 0x00000068 -#define REG_HDMI_8960_PHY_PLL_DEBUG_SEL 0x0000056c +#define REG_HDMI_8960_PHY_PLL_DEBUG_SEL 0x0000006c -#define REG_HDMI_8960_PHY_PLL_MISC0 0x00000570 +#define REG_HDMI_8960_PHY_PLL_MISC0 0x00000070 -#define REG_HDMI_8960_PHY_PLL_MISC1 0x00000574 +#define REG_HDMI_8960_PHY_PLL_MISC1 0x00000074 -#define REG_HDMI_8960_PHY_PLL_MISC2 0x00000578 +#define REG_HDMI_8960_PHY_PLL_MISC2 0x00000078 -#define REG_HDMI_8960_PHY_PLL_MISC3 0x0000057c +#define REG_HDMI_8960_PHY_PLL_MISC3 0x0000007c -#define REG_HDMI_8960_PHY_PLL_MISC4 0x00000580 +#define REG_HDMI_8960_PHY_PLL_MISC4 0x00000080 -#define REG_HDMI_8960_PHY_PLL_MISC5 0x00000584 +#define REG_HDMI_8960_PHY_PLL_MISC5 0x00000084 -#define REG_HDMI_8960_PHY_PLL_MISC6 0x00000588 +#define REG_HDMI_8960_PHY_PLL_MISC6 0x00000088 -#define REG_HDMI_8960_PHY_PLL_DEBUG_BUS0 0x0000058c +#define REG_HDMI_8960_PHY_PLL_DEBUG_BUS0 0x0000008c -#define REG_HDMI_8960_PHY_PLL_DEBUG_BUS1 0x00000590 +#define REG_HDMI_8960_PHY_PLL_DEBUG_BUS1 0x00000090 -#define REG_HDMI_8960_PHY_PLL_DEBUG_BUS2 0x00000594 +#define REG_HDMI_8960_PHY_PLL_DEBUG_BUS2 0x00000094 -#define REG_HDMI_8960_PHY_PLL_STATUS0 0x00000598 +#define REG_HDMI_8960_PHY_PLL_STATUS0 0x00000098 #define HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK 0x00000001 -#define REG_HDMI_8960_PHY_PLL_STATUS1 0x0000059c +#define REG_HDMI_8960_PHY_PLL_STATUS1 0x0000009c #define REG_HDMI_8x74_ANA_CFG0 0x00000000 @@ -843,5 +844,501 @@ static inline uint32_t HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(uint32_t val) #define REG_HDMI_28nm_PHY_PLL_DEBUG_BUS_SEL 0x000000a0 +#define REG_HDMI_8996_PHY_CFG 0x00000000 + +#define REG_HDMI_8996_PHY_PD_CTL 0x00000004 + +#define REG_HDMI_8996_PHY_MODE 0x00000008 + +#define REG_HDMI_8996_PHY_MISR_CLEAR 0x0000000c + +#define REG_HDMI_8996_PHY_TX0_TX1_BIST_CFG0 0x00000010 + +#define REG_HDMI_8996_PHY_TX0_TX1_BIST_CFG1 0x00000014 + +#define REG_HDMI_8996_PHY_TX0_TX1_PRBS_SEED_BYTE0 0x00000018 + +#define REG_HDMI_8996_PHY_TX0_TX1_PRBS_SEED_BYTE1 0x0000001c + +#define REG_HDMI_8996_PHY_TX0_TX1_BIST_PATTERN0 0x00000020 + +#define REG_HDMI_8996_PHY_TX0_TX1_BIST_PATTERN1 0x00000024 + +#define REG_HDMI_8996_PHY_TX2_TX3_BIST_CFG0 0x00000028 + +#define REG_HDMI_8996_PHY_TX2_TX3_BIST_CFG1 0x0000002c + +#define REG_HDMI_8996_PHY_TX2_TX3_PRBS_SEED_BYTE0 0x00000030 + +#define REG_HDMI_8996_PHY_TX2_TX3_PRBS_SEED_BYTE1 0x00000034 + +#define REG_HDMI_8996_PHY_TX2_TX3_BIST_PATTERN0 0x00000038 + +#define REG_HDMI_8996_PHY_TX2_TX3_BIST_PATTERN1 0x0000003c + +#define REG_HDMI_8996_PHY_DEBUG_BUS_SEL 0x00000040 + +#define REG_HDMI_8996_PHY_TXCAL_CFG0 0x00000044 + +#define REG_HDMI_8996_PHY_TXCAL_CFG1 0x00000048 + +#define REG_HDMI_8996_PHY_TX0_TX1_LANE_CTL 0x0000004c + +#define REG_HDMI_8996_PHY_TX2_TX3_LANE_CTL 0x00000050 + +#define REG_HDMI_8996_PHY_LANE_BIST_CONFIG 0x00000054 + +#define REG_HDMI_8996_PHY_CLOCK 0x00000058 + +#define REG_HDMI_8996_PHY_MISC1 0x0000005c + +#define REG_HDMI_8996_PHY_MISC2 0x00000060 + +#define REG_HDMI_8996_PHY_TX0_TX1_BIST_STATUS0 0x00000064 + +#define REG_HDMI_8996_PHY_TX0_TX1_BIST_STATUS1 0x00000068 + +#define REG_HDMI_8996_PHY_TX0_TX1_BIST_STATUS2 0x0000006c + +#define REG_HDMI_8996_PHY_TX2_TX3_BIST_STATUS0 0x00000070 + +#define REG_HDMI_8996_PHY_TX2_TX3_BIST_STATUS1 0x00000074 + +#define REG_HDMI_8996_PHY_TX2_TX3_BIST_STATUS2 0x00000078 + +#define REG_HDMI_8996_PHY_PRE_MISR_STATUS0 0x0000007c + +#define REG_HDMI_8996_PHY_PRE_MISR_STATUS1 0x00000080 + +#define REG_HDMI_8996_PHY_PRE_MISR_STATUS2 0x00000084 + +#define REG_HDMI_8996_PHY_PRE_MISR_STATUS3 0x00000088 + +#define REG_HDMI_8996_PHY_POST_MISR_STATUS0 0x0000008c + +#define REG_HDMI_8996_PHY_POST_MISR_STATUS1 0x00000090 + +#define REG_HDMI_8996_PHY_POST_MISR_STATUS2 0x00000094 + +#define REG_HDMI_8996_PHY_POST_MISR_STATUS3 0x00000098 + +#define REG_HDMI_8996_PHY_STATUS 0x0000009c + +#define REG_HDMI_8996_PHY_MISC3_STATUS 0x000000a0 + +#define REG_HDMI_8996_PHY_MISC4_STATUS 0x000000a4 + +#define REG_HDMI_8996_PHY_DEBUG_BUS0 0x000000a8 + +#define REG_HDMI_8996_PHY_DEBUG_BUS1 0x000000ac + +#define REG_HDMI_8996_PHY_DEBUG_BUS2 0x000000b0 + +#define REG_HDMI_8996_PHY_DEBUG_BUS3 0x000000b4 + +#define REG_HDMI_8996_PHY_PHY_REVISION_ID0 0x000000b8 + +#define REG_HDMI_8996_PHY_PHY_REVISION_ID1 0x000000bc + +#define REG_HDMI_8996_PHY_PHY_REVISION_ID2 0x000000c0 + +#define REG_HDMI_8996_PHY_PHY_REVISION_ID3 0x000000c4 + +#define REG_HDMI_PHY_QSERDES_COM_ATB_SEL1 0x00000000 + +#define REG_HDMI_PHY_QSERDES_COM_ATB_SEL2 0x00000004 + +#define REG_HDMI_PHY_QSERDES_COM_FREQ_UPDATE 0x00000008 + +#define REG_HDMI_PHY_QSERDES_COM_BG_TIMER 0x0000000c + +#define REG_HDMI_PHY_QSERDES_COM_SSC_EN_CENTER 0x00000010 + +#define REG_HDMI_PHY_QSERDES_COM_SSC_ADJ_PER1 0x00000014 + +#define REG_HDMI_PHY_QSERDES_COM_SSC_ADJ_PER2 0x00000018 + +#define REG_HDMI_PHY_QSERDES_COM_SSC_PER1 0x0000001c + +#define REG_HDMI_PHY_QSERDES_COM_SSC_PER2 0x00000020 + +#define REG_HDMI_PHY_QSERDES_COM_SSC_STEP_SIZE1 0x00000024 + +#define REG_HDMI_PHY_QSERDES_COM_SSC_STEP_SIZE2 0x00000028 + +#define REG_HDMI_PHY_QSERDES_COM_POST_DIV 0x0000002c + +#define REG_HDMI_PHY_QSERDES_COM_POST_DIV_MUX 0x00000030 + +#define REG_HDMI_PHY_QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x00000034 + +#define REG_HDMI_PHY_QSERDES_COM_CLK_ENABLE1 0x00000038 + +#define REG_HDMI_PHY_QSERDES_COM_SYS_CLK_CTRL 0x0000003c + +#define REG_HDMI_PHY_QSERDES_COM_SYSCLK_BUF_ENABLE 0x00000040 + +#define REG_HDMI_PHY_QSERDES_COM_PLL_EN 0x00000044 + +#define REG_HDMI_PHY_QSERDES_COM_PLL_IVCO 0x00000048 + +#define REG_HDMI_PHY_QSERDES_COM_LOCK_CMP1_MODE0 0x0000004c + +#define REG_HDMI_PHY_QSERDES_COM_LOCK_CMP2_MODE0 0x00000050 + +#define REG_HDMI_PHY_QSERDES_COM_LOCK_CMP3_MODE0 0x00000054 + +#define REG_HDMI_PHY_QSERDES_COM_LOCK_CMP1_MODE1 0x00000058 + +#define REG_HDMI_PHY_QSERDES_COM_LOCK_CMP2_MODE1 0x0000005c + +#define REG_HDMI_PHY_QSERDES_COM_LOCK_CMP3_MODE1 0x00000060 + +#define REG_HDMI_PHY_QSERDES_COM_LOCK_CMP1_MODE2 0x00000064 + +#define REG_HDMI_PHY_QSERDES_COM_CMN_RSVD0 0x00000064 + +#define REG_HDMI_PHY_QSERDES_COM_LOCK_CMP2_MODE2 0x00000068 + +#define REG_HDMI_PHY_QSERDES_COM_EP_CLOCK_DETECT_CTRL 0x00000068 + +#define REG_HDMI_PHY_QSERDES_COM_LOCK_CMP3_MODE2 0x0000006c + +#define REG_HDMI_PHY_QSERDES_COM_SYSCLK_DET_COMP_STATUS 0x0000006c + +#define REG_HDMI_PHY_QSERDES_COM_BG_TRIM 0x00000070 + +#define REG_HDMI_PHY_QSERDES_COM_CLK_EP_DIV 0x00000074 + +#define REG_HDMI_PHY_QSERDES_COM_CP_CTRL_MODE0 0x00000078 + +#define REG_HDMI_PHY_QSERDES_COM_CP_CTRL_MODE1 0x0000007c + +#define REG_HDMI_PHY_QSERDES_COM_CP_CTRL_MODE2 0x00000080 + +#define REG_HDMI_PHY_QSERDES_COM_CMN_RSVD1 0x00000080 + +#define REG_HDMI_PHY_QSERDES_COM_PLL_RCTRL_MODE0 0x00000084 + +#define REG_HDMI_PHY_QSERDES_COM_PLL_RCTRL_MODE1 0x00000088 + +#define REG_HDMI_PHY_QSERDES_COM_PLL_RCTRL_MODE2 0x0000008c + +#define REG_HDMI_PHY_QSERDES_COM_CMN_RSVD2 0x0000008c + +#define REG_HDMI_PHY_QSERDES_COM_PLL_CCTRL_MODE0 0x00000090 + +#define REG_HDMI_PHY_QSERDES_COM_PLL_CCTRL_MODE1 0x00000094 + +#define REG_HDMI_PHY_QSERDES_COM_PLL_CCTRL_MODE2 0x00000098 + +#define REG_HDMI_PHY_QSERDES_COM_CMN_RSVD3 0x00000098 + +#define REG_HDMI_PHY_QSERDES_COM_PLL_CNTRL 0x0000009c + +#define REG_HDMI_PHY_QSERDES_COM_PHASE_SEL_CTRL 0x000000a0 + +#define REG_HDMI_PHY_QSERDES_COM_PHASE_SEL_DC 0x000000a4 + +#define REG_HDMI_PHY_QSERDES_COM_CORE_CLK_IN_SYNC_SEL 0x000000a8 + +#define REG_HDMI_PHY_QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x000000a8 + +#define REG_HDMI_PHY_QSERDES_COM_SYSCLK_EN_SEL 0x000000ac + +#define REG_HDMI_PHY_QSERDES_COM_CML_SYSCLK_SEL 0x000000b0 + +#define REG_HDMI_PHY_QSERDES_COM_RESETSM_CNTRL 0x000000b4 + +#define REG_HDMI_PHY_QSERDES_COM_RESETSM_CNTRL2 0x000000b8 + +#define REG_HDMI_PHY_QSERDES_COM_RESTRIM_CTRL 0x000000bc + +#define REG_HDMI_PHY_QSERDES_COM_RESTRIM_CTRL2 0x000000c0 + +#define REG_HDMI_PHY_QSERDES_COM_RESCODE_DIV_NUM 0x000000c4 + +#define REG_HDMI_PHY_QSERDES_COM_LOCK_CMP_EN 0x000000c8 + +#define REG_HDMI_PHY_QSERDES_COM_LOCK_CMP_CFG 0x000000cc + +#define REG_HDMI_PHY_QSERDES_COM_DEC_START_MODE0 0x000000d0 + +#define REG_HDMI_PHY_QSERDES_COM_DEC_START_MODE1 0x000000d4 + +#define REG_HDMI_PHY_QSERDES_COM_DEC_START_MODE2 0x000000d8 + +#define REG_HDMI_PHY_QSERDES_COM_VCOCAL_DEADMAN_CTRL 0x000000d8 + +#define REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START1_MODE0 0x000000dc + +#define REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START2_MODE0 0x000000e0 + +#define REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START3_MODE0 0x000000e4 + +#define REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START1_MODE1 0x000000e8 + +#define REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START2_MODE1 0x000000ec + +#define REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START3_MODE1 0x000000f0 + +#define REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START1_MODE2 0x000000f4 + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_MINVAL1 0x000000f4 + +#define REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START2_MODE2 0x000000f8 + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_MINVAL2 0x000000f8 + +#define REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START3_MODE2 0x000000fc + +#define REG_HDMI_PHY_QSERDES_COM_CMN_RSVD4 0x000000fc + +#define REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_INITVAL 0x00000100 + +#define REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_EN 0x00000104 + +#define REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x00000108 + +#define REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x0000010c + +#define REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x00000110 + +#define REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x00000114 + +#define REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_GAIN0_MODE2 0x00000118 + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_MAXVAL1 0x00000118 + +#define REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_GAIN1_MODE2 0x0000011c + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_MAXVAL2 0x0000011c + +#define REG_HDMI_PHY_QSERDES_COM_RES_TRIM_CONTROL2 0x00000120 + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_CTRL 0x00000124 + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_MAP 0x00000128 + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE1_MODE0 0x0000012c + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE2_MODE0 0x00000130 + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE1_MODE1 0x00000134 + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE2_MODE1 0x00000138 + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE1_MODE2 0x0000013c + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_INITVAL1 0x0000013c + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE2_MODE2 0x00000140 + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_INITVAL2 0x00000140 + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_TIMER1 0x00000144 + +#define REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_TIMER2 0x00000148 + +#define REG_HDMI_PHY_QSERDES_COM_SAR 0x0000014c + +#define REG_HDMI_PHY_QSERDES_COM_SAR_CLK 0x00000150 + +#define REG_HDMI_PHY_QSERDES_COM_SAR_CODE_OUT_STATUS 0x00000154 + +#define REG_HDMI_PHY_QSERDES_COM_SAR_CODE_READY_STATUS 0x00000158 + +#define REG_HDMI_PHY_QSERDES_COM_CMN_STATUS 0x0000015c + +#define REG_HDMI_PHY_QSERDES_COM_RESET_SM_STATUS 0x00000160 + +#define REG_HDMI_PHY_QSERDES_COM_RESTRIM_CODE_STATUS 0x00000164 + +#define REG_HDMI_PHY_QSERDES_COM_PLLCAL_CODE1_STATUS 0x00000168 + +#define REG_HDMI_PHY_QSERDES_COM_PLLCAL_CODE2_STATUS 0x0000016c + +#define REG_HDMI_PHY_QSERDES_COM_BG_CTRL 0x00000170 + +#define REG_HDMI_PHY_QSERDES_COM_CLK_SELECT 0x00000174 + +#define REG_HDMI_PHY_QSERDES_COM_HSCLK_SEL 0x00000178 + +#define REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_BINCODE_STATUS 0x0000017c + +#define REG_HDMI_PHY_QSERDES_COM_PLL_ANALOG 0x00000180 + +#define REG_HDMI_PHY_QSERDES_COM_CORECLK_DIV 0x00000184 + +#define REG_HDMI_PHY_QSERDES_COM_SW_RESET 0x00000188 + +#define REG_HDMI_PHY_QSERDES_COM_CORE_CLK_EN 0x0000018c + +#define REG_HDMI_PHY_QSERDES_COM_C_READY_STATUS 0x00000190 + +#define REG_HDMI_PHY_QSERDES_COM_CMN_CONFIG 0x00000194 + +#define REG_HDMI_PHY_QSERDES_COM_CMN_RATE_OVERRIDE 0x00000198 + +#define REG_HDMI_PHY_QSERDES_COM_SVS_MODE_CLK_SEL 0x0000019c + +#define REG_HDMI_PHY_QSERDES_COM_DEBUG_BUS0 0x000001a0 + +#define REG_HDMI_PHY_QSERDES_COM_DEBUG_BUS1 0x000001a4 + +#define REG_HDMI_PHY_QSERDES_COM_DEBUG_BUS2 0x000001a8 + +#define REG_HDMI_PHY_QSERDES_COM_DEBUG_BUS3 0x000001ac + +#define REG_HDMI_PHY_QSERDES_COM_DEBUG_BUS_SEL 0x000001b0 + +#define REG_HDMI_PHY_QSERDES_COM_CMN_MISC1 0x000001b4 + +#define REG_HDMI_PHY_QSERDES_COM_CMN_MISC2 0x000001b8 + +#define REG_HDMI_PHY_QSERDES_COM_CORECLK_DIV_MODE1 0x000001bc + +#define REG_HDMI_PHY_QSERDES_COM_CORECLK_DIV_MODE2 0x000001c0 + +#define REG_HDMI_PHY_QSERDES_COM_CMN_RSVD5 0x000001c4 + +#define REG_HDMI_PHY_QSERDES_TX_LX_BIST_MODE_LANENO 0x00000000 + +#define REG_HDMI_PHY_QSERDES_TX_LX_BIST_INVERT 0x00000004 + +#define REG_HDMI_PHY_QSERDES_TX_LX_CLKBUF_ENABLE 0x00000008 + +#define REG_HDMI_PHY_QSERDES_TX_LX_CMN_CONTROL_ONE 0x0000000c + +#define REG_HDMI_PHY_QSERDES_TX_LX_CMN_CONTROL_TWO 0x00000010 + +#define REG_HDMI_PHY_QSERDES_TX_LX_CMN_CONTROL_THREE 0x00000014 + +#define REG_HDMI_PHY_QSERDES_TX_LX_TX_EMP_POST1_LVL 0x00000018 + +#define REG_HDMI_PHY_QSERDES_TX_LX_TX_POST2_EMPH 0x0000001c + +#define REG_HDMI_PHY_QSERDES_TX_LX_TX_BOOST_LVL_UP_DN 0x00000020 + +#define REG_HDMI_PHY_QSERDES_TX_LX_HP_PD_ENABLES 0x00000024 + +#define REG_HDMI_PHY_QSERDES_TX_LX_TX_IDLE_LVL_LARGE_AMP 0x00000028 + +#define REG_HDMI_PHY_QSERDES_TX_LX_TX_DRV_LVL 0x0000002c + +#define REG_HDMI_PHY_QSERDES_TX_LX_TX_DRV_LVL_OFFSET 0x00000030 + +#define REG_HDMI_PHY_QSERDES_TX_LX_RESET_TSYNC_EN 0x00000034 + +#define REG_HDMI_PHY_QSERDES_TX_LX_PRE_STALL_LDO_BOOST_EN 0x00000038 + +#define REG_HDMI_PHY_QSERDES_TX_LX_TX_BAND 0x0000003c + +#define REG_HDMI_PHY_QSERDES_TX_LX_SLEW_CNTL 0x00000040 + +#define REG_HDMI_PHY_QSERDES_TX_LX_INTERFACE_SELECT 0x00000044 + +#define REG_HDMI_PHY_QSERDES_TX_LX_LPB_EN 0x00000048 + +#define REG_HDMI_PHY_QSERDES_TX_LX_RES_CODE_LANE_TX 0x0000004c + +#define REG_HDMI_PHY_QSERDES_TX_LX_RES_CODE_LANE_RX 0x00000050 + +#define REG_HDMI_PHY_QSERDES_TX_LX_RES_CODE_LANE_OFFSET 0x00000054 + +#define REG_HDMI_PHY_QSERDES_TX_LX_PERL_LENGTH1 0x00000058 + +#define REG_HDMI_PHY_QSERDES_TX_LX_PERL_LENGTH2 0x0000005c + +#define REG_HDMI_PHY_QSERDES_TX_LX_SERDES_BYP_EN_OUT 0x00000060 + +#define REG_HDMI_PHY_QSERDES_TX_LX_DEBUG_BUS_SEL 0x00000064 + +#define REG_HDMI_PHY_QSERDES_TX_LX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x00000068 + +#define REG_HDMI_PHY_QSERDES_TX_LX_TX_POL_INV 0x0000006c + +#define REG_HDMI_PHY_QSERDES_TX_LX_PARRATE_REC_DETECT_IDLE_EN 0x00000070 + +#define REG_HDMI_PHY_QSERDES_TX_LX_BIST_PATTERN1 0x00000074 + +#define REG_HDMI_PHY_QSERDES_TX_LX_BIST_PATTERN2 0x00000078 + +#define REG_HDMI_PHY_QSERDES_TX_LX_BIST_PATTERN3 0x0000007c + +#define REG_HDMI_PHY_QSERDES_TX_LX_BIST_PATTERN4 0x00000080 + +#define REG_HDMI_PHY_QSERDES_TX_LX_BIST_PATTERN5 0x00000084 + +#define REG_HDMI_PHY_QSERDES_TX_LX_BIST_PATTERN6 0x00000088 + +#define REG_HDMI_PHY_QSERDES_TX_LX_BIST_PATTERN7 0x0000008c + +#define REG_HDMI_PHY_QSERDES_TX_LX_BIST_PATTERN8 0x00000090 + +#define REG_HDMI_PHY_QSERDES_TX_LX_LANE_MODE 0x00000094 + +#define REG_HDMI_PHY_QSERDES_TX_LX_IDAC_CAL_LANE_MODE 0x00000098 + +#define REG_HDMI_PHY_QSERDES_TX_LX_IDAC_CAL_LANE_MODE_CONFIGURATION 0x0000009c + +#define REG_HDMI_PHY_QSERDES_TX_LX_ATB_SEL1 0x000000a0 + +#define REG_HDMI_PHY_QSERDES_TX_LX_ATB_SEL2 0x000000a4 + +#define REG_HDMI_PHY_QSERDES_TX_LX_RCV_DETECT_LVL 0x000000a8 + +#define REG_HDMI_PHY_QSERDES_TX_LX_RCV_DETECT_LVL_2 0x000000ac + +#define REG_HDMI_PHY_QSERDES_TX_LX_PRBS_SEED1 0x000000b0 + +#define REG_HDMI_PHY_QSERDES_TX_LX_PRBS_SEED2 0x000000b4 + +#define REG_HDMI_PHY_QSERDES_TX_LX_PRBS_SEED3 0x000000b8 + +#define REG_HDMI_PHY_QSERDES_TX_LX_PRBS_SEED4 0x000000bc + +#define REG_HDMI_PHY_QSERDES_TX_LX_RESET_GEN 0x000000c0 + +#define REG_HDMI_PHY_QSERDES_TX_LX_RESET_GEN_MUXES 0x000000c4 + +#define REG_HDMI_PHY_QSERDES_TX_LX_TRAN_DRVR_EMP_EN 0x000000c8 + +#define REG_HDMI_PHY_QSERDES_TX_LX_TX_INTERFACE_MODE 0x000000cc + +#define REG_HDMI_PHY_QSERDES_TX_LX_PWM_CTRL 0x000000d0 + +#define REG_HDMI_PHY_QSERDES_TX_LX_PWM_ENCODED_OR_DATA 0x000000d4 + +#define REG_HDMI_PHY_QSERDES_TX_LX_PWM_GEAR_1_DIVIDER_BAND2 0x000000d8 + +#define REG_HDMI_PHY_QSERDES_TX_LX_PWM_GEAR_2_DIVIDER_BAND2 0x000000dc + +#define REG_HDMI_PHY_QSERDES_TX_LX_PWM_GEAR_3_DIVIDER_BAND2 0x000000e0 + +#define REG_HDMI_PHY_QSERDES_TX_LX_PWM_GEAR_4_DIVIDER_BAND2 0x000000e4 + +#define REG_HDMI_PHY_QSERDES_TX_LX_PWM_GEAR_1_DIVIDER_BAND0_1 0x000000e8 + +#define REG_HDMI_PHY_QSERDES_TX_LX_PWM_GEAR_2_DIVIDER_BAND0_1 0x000000ec + +#define REG_HDMI_PHY_QSERDES_TX_LX_PWM_GEAR_3_DIVIDER_BAND0_1 0x000000f0 + +#define REG_HDMI_PHY_QSERDES_TX_LX_PWM_GEAR_4_DIVIDER_BAND0_1 0x000000f4 + +#define REG_HDMI_PHY_QSERDES_TX_LX_VMODE_CTRL1 0x000000f8 + +#define REG_HDMI_PHY_QSERDES_TX_LX_VMODE_CTRL2 0x000000fc + +#define REG_HDMI_PHY_QSERDES_TX_LX_TX_ALOG_INTF_OBSV_CNTL 0x00000100 + +#define REG_HDMI_PHY_QSERDES_TX_LX_BIST_STATUS 0x00000104 + +#define REG_HDMI_PHY_QSERDES_TX_LX_BIST_ERROR_COUNT1 0x00000108 + +#define REG_HDMI_PHY_QSERDES_TX_LX_BIST_ERROR_COUNT2 0x0000010c + +#define REG_HDMI_PHY_QSERDES_TX_LX_TX_ALOG_INTF_OBSV 0x00000110 + #endif /* HDMI_XML */ diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c index df232e20c13e..a54d3bb5baad 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_audio.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_audio.c @@ -89,7 +89,7 @@ static const struct hdmi_msm_audio_arcs *get_arcs(unsigned long int pixclock) return NULL; } -int hdmi_audio_update(struct hdmi *hdmi) +int msm_hdmi_audio_update(struct hdmi *hdmi) { struct hdmi_audio *audio = &hdmi->audio; struct hdmi_audio_infoframe *info = &audio->infoframe; @@ -232,7 +232,7 @@ int hdmi_audio_update(struct hdmi *hdmi) return 0; } -int hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled, +int msm_hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled, uint32_t num_of_channels, uint32_t channel_allocation, uint32_t level_shift, bool down_mix) { @@ -252,10 +252,10 @@ int hdmi_audio_info_setup(struct hdmi *hdmi, bool enabled, audio->infoframe.level_shift_value = level_shift; audio->infoframe.downmix_inhibit = down_mix; - return hdmi_audio_update(hdmi); + return msm_hdmi_audio_update(hdmi); } -void hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate) +void msm_hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate) { struct hdmi_audio *audio; @@ -268,5 +268,5 @@ void hdmi_audio_set_sample_rate(struct hdmi *hdmi, int rate) return; audio->rate = rate; - hdmi_audio_update(hdmi); + msm_hdmi_audio_update(hdmi); } diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index 92b69ae8caf9..bacbd5d8df0e 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -23,11 +23,11 @@ struct hdmi_bridge { }; #define to_hdmi_bridge(x) container_of(x, struct hdmi_bridge, base) -void hdmi_bridge_destroy(struct drm_bridge *bridge) +void msm_hdmi_bridge_destroy(struct drm_bridge *bridge) { } -static void power_on(struct drm_bridge *bridge) +static void msm_hdmi_power_on(struct drm_bridge *bridge) { struct drm_device *dev = bridge->dev; struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); @@ -86,7 +86,7 @@ static void power_off(struct drm_bridge *bridge) } } -static void hdmi_bridge_pre_enable(struct drm_bridge *bridge) +static void msm_hdmi_bridge_pre_enable(struct drm_bridge *bridge) { struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; @@ -95,51 +95,51 @@ static void hdmi_bridge_pre_enable(struct drm_bridge *bridge) DBG("power up"); if (!hdmi->power_on) { - power_on(bridge); + msm_hdmi_phy_resource_enable(phy); + msm_hdmi_power_on(bridge); hdmi->power_on = true; - hdmi_audio_update(hdmi); + msm_hdmi_audio_update(hdmi); } - if (phy) - phy->funcs->powerup(phy, hdmi->pixclock); + msm_hdmi_phy_powerup(phy, hdmi->pixclock); - hdmi_set_mode(hdmi, true); + msm_hdmi_set_mode(hdmi, true); if (hdmi->hdcp_ctrl) - hdmi_hdcp_on(hdmi->hdcp_ctrl); + msm_hdmi_hdcp_on(hdmi->hdcp_ctrl); } -static void hdmi_bridge_enable(struct drm_bridge *bridge) +static void msm_hdmi_bridge_enable(struct drm_bridge *bridge) { } -static void hdmi_bridge_disable(struct drm_bridge *bridge) +static void msm_hdmi_bridge_disable(struct drm_bridge *bridge) { } -static void hdmi_bridge_post_disable(struct drm_bridge *bridge) +static void msm_hdmi_bridge_post_disable(struct drm_bridge *bridge) { struct hdmi_bridge *hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = hdmi_bridge->hdmi; struct hdmi_phy *phy = hdmi->phy; if (hdmi->hdcp_ctrl) - hdmi_hdcp_off(hdmi->hdcp_ctrl); + msm_hdmi_hdcp_off(hdmi->hdcp_ctrl); DBG("power down"); - hdmi_set_mode(hdmi, false); + msm_hdmi_set_mode(hdmi, false); - if (phy) - phy->funcs->powerdown(phy); + msm_hdmi_phy_powerdown(phy); if (hdmi->power_on) { power_off(bridge); hdmi->power_on = false; - hdmi_audio_update(hdmi); + msm_hdmi_audio_update(hdmi); + msm_hdmi_phy_resource_disable(phy); } } -static void hdmi_bridge_mode_set(struct drm_bridge *bridge, +static void msm_hdmi_bridge_mode_set(struct drm_bridge *bridge, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { @@ -196,20 +196,20 @@ static void hdmi_bridge_mode_set(struct drm_bridge *bridge, DBG("frame_ctrl=%08x", frame_ctrl); hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl); - hdmi_audio_update(hdmi); + msm_hdmi_audio_update(hdmi); } -static const struct drm_bridge_funcs hdmi_bridge_funcs = { - .pre_enable = hdmi_bridge_pre_enable, - .enable = hdmi_bridge_enable, - .disable = hdmi_bridge_disable, - .post_disable = hdmi_bridge_post_disable, - .mode_set = hdmi_bridge_mode_set, +static const struct drm_bridge_funcs msm_hdmi_bridge_funcs = { + .pre_enable = msm_hdmi_bridge_pre_enable, + .enable = msm_hdmi_bridge_enable, + .disable = msm_hdmi_bridge_disable, + .post_disable = msm_hdmi_bridge_post_disable, + .mode_set = msm_hdmi_bridge_mode_set, }; /* initialize bridge */ -struct drm_bridge *hdmi_bridge_init(struct hdmi *hdmi) +struct drm_bridge *msm_hdmi_bridge_init(struct hdmi *hdmi) { struct drm_bridge *bridge = NULL; struct hdmi_bridge *hdmi_bridge; @@ -225,7 +225,7 @@ struct drm_bridge *hdmi_bridge_init(struct hdmi *hdmi) hdmi_bridge->hdmi = hdmi; bridge = &hdmi_bridge->base; - bridge->funcs = &hdmi_bridge_funcs; + bridge->funcs = &msm_hdmi_bridge_funcs; ret = drm_bridge_attach(hdmi->dev, bridge); if (ret) @@ -235,7 +235,7 @@ struct drm_bridge *hdmi_bridge_init(struct hdmi *hdmi) fail: if (bridge) - hdmi_bridge_destroy(bridge); + msm_hdmi_bridge_destroy(bridge); return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c index a3b05ae52dae..26129bff2dd6 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c @@ -28,7 +28,7 @@ struct hdmi_connector { }; #define to_hdmi_connector(x) container_of(x, struct hdmi_connector, base) -static void hdmi_phy_reset(struct hdmi *hdmi) +static void msm_hdmi_phy_reset(struct hdmi *hdmi) { unsigned int val; @@ -81,114 +81,54 @@ static int gpio_config(struct hdmi *hdmi, bool on) { struct device *dev = &hdmi->pdev->dev; const struct hdmi_platform_config *config = hdmi->config; - int ret; + int ret, i; if (on) { - if (config->ddc_clk_gpio != -1) { - ret = gpio_request(config->ddc_clk_gpio, "HDMI_DDC_CLK"); - if (ret) { - dev_err(dev, "'%s'(%d) gpio_request failed: %d\n", - "HDMI_DDC_CLK", config->ddc_clk_gpio, ret); - goto error1; - } - gpio_set_value_cansleep(config->ddc_clk_gpio, 1); - } - - if (config->ddc_data_gpio != -1) { - ret = gpio_request(config->ddc_data_gpio, "HDMI_DDC_DATA"); - if (ret) { - dev_err(dev, "'%s'(%d) gpio_request failed: %d\n", - "HDMI_DDC_DATA", config->ddc_data_gpio, ret); - goto error2; - } - gpio_set_value_cansleep(config->ddc_data_gpio, 1); - } - - ret = gpio_request(config->hpd_gpio, "HDMI_HPD"); - if (ret) { - dev_err(dev, "'%s'(%d) gpio_request failed: %d\n", - "HDMI_HPD", config->hpd_gpio, ret); - goto error3; - } - gpio_direction_input(config->hpd_gpio); - gpio_set_value_cansleep(config->hpd_gpio, 1); - - if (config->mux_en_gpio != -1) { - ret = gpio_request(config->mux_en_gpio, "HDMI_MUX_EN"); - if (ret) { - dev_err(dev, "'%s'(%d) gpio_request failed: %d\n", - "HDMI_MUX_EN", config->mux_en_gpio, ret); - goto error4; - } - gpio_set_value_cansleep(config->mux_en_gpio, 1); - } - - if (config->mux_sel_gpio != -1) { - ret = gpio_request(config->mux_sel_gpio, "HDMI_MUX_SEL"); - if (ret) { - dev_err(dev, "'%s'(%d) gpio_request failed: %d\n", - "HDMI_MUX_SEL", config->mux_sel_gpio, ret); - goto error5; + for (i = 0; i < HDMI_MAX_NUM_GPIO; i++) { + struct hdmi_gpio_data gpio = config->gpios[i]; + + if (gpio.num != -1) { + ret = gpio_request(gpio.num, gpio.label); + if (ret) { + dev_err(dev, + "'%s'(%d) gpio_request failed: %d\n", + gpio.label, gpio.num, ret); + goto err; + } + + if (gpio.output) { + gpio_direction_output(gpio.num, + gpio.value); + } else { + gpio_direction_input(gpio.num); + gpio_set_value_cansleep(gpio.num, + gpio.value); + } } - gpio_set_value_cansleep(config->mux_sel_gpio, 0); } - if (config->mux_lpm_gpio != -1) { - ret = gpio_request(config->mux_lpm_gpio, - "HDMI_MUX_LPM"); - if (ret) { - dev_err(dev, - "'%s'(%d) gpio_request failed: %d\n", - "HDMI_MUX_LPM", - config->mux_lpm_gpio, ret); - goto error6; - } - gpio_set_value_cansleep(config->mux_lpm_gpio, 1); - } DBG("gpio on"); } else { - if (config->ddc_clk_gpio != -1) - gpio_free(config->ddc_clk_gpio); - - if (config->ddc_data_gpio != -1) - gpio_free(config->ddc_data_gpio); + for (i = 0; i < HDMI_MAX_NUM_GPIO; i++) { + struct hdmi_gpio_data gpio = config->gpios[i]; - gpio_free(config->hpd_gpio); + if (gpio.output) { + int value = gpio.value ? 0 : 1; - if (config->mux_en_gpio != -1) { - gpio_set_value_cansleep(config->mux_en_gpio, 0); - gpio_free(config->mux_en_gpio); - } + gpio_set_value_cansleep(gpio.num, value); + } - if (config->mux_sel_gpio != -1) { - gpio_set_value_cansleep(config->mux_sel_gpio, 1); - gpio_free(config->mux_sel_gpio); - } + gpio_free(gpio.num); + }; - if (config->mux_lpm_gpio != -1) { - gpio_set_value_cansleep(config->mux_lpm_gpio, 0); - gpio_free(config->mux_lpm_gpio); - } DBG("gpio off"); } return 0; +err: + while (i--) + gpio_free(config->gpios[i].num); -error6: - if (config->mux_sel_gpio != -1) - gpio_free(config->mux_sel_gpio); -error5: - if (config->mux_en_gpio != -1) - gpio_free(config->mux_en_gpio); -error4: - gpio_free(config->hpd_gpio); -error3: - if (config->ddc_data_gpio != -1) - gpio_free(config->ddc_data_gpio); -error2: - if (config->ddc_clk_gpio != -1) - gpio_free(config->ddc_clk_gpio); -error1: return ret; } @@ -239,9 +179,9 @@ static int hpd_enable(struct hdmi_connector *hdmi_connector) } } - hdmi_set_mode(hdmi, false); - hdmi_phy_reset(hdmi); - hdmi_set_mode(hdmi, true); + msm_hdmi_set_mode(hdmi, false); + msm_hdmi_phy_reset(hdmi); + msm_hdmi_set_mode(hdmi, true); hdmi_write(hdmi, REG_HDMI_USEC_REFTIMER, 0x0001001b); @@ -278,7 +218,7 @@ static void hdp_disable(struct hdmi_connector *hdmi_connector) /* Disable HPD interrupt */ hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0); - hdmi_set_mode(hdmi, false); + msm_hdmi_set_mode(hdmi, false); for (i = 0; i < config->hpd_clk_cnt; i++) clk_disable_unprepare(hdmi->hpd_clks[i]); @@ -300,7 +240,7 @@ static void hdp_disable(struct hdmi_connector *hdmi_connector) } static void -hotplug_work(struct work_struct *work) +msm_hdmi_hotplug_work(struct work_struct *work) { struct hdmi_connector *hdmi_connector = container_of(work, struct hdmi_connector, hpd_work); @@ -308,7 +248,7 @@ hotplug_work(struct work_struct *work) drm_helper_hpd_irq_event(connector->dev); } -void hdmi_connector_irq(struct drm_connector *connector) +void msm_hdmi_connector_irq(struct drm_connector *connector) { struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector); struct hdmi *hdmi = hdmi_connector->hdmi; @@ -345,10 +285,13 @@ static enum drm_connector_status detect_reg(struct hdmi *hdmi) connector_status_connected : connector_status_disconnected; } +#define HPD_GPIO_INDEX 2 static enum drm_connector_status detect_gpio(struct hdmi *hdmi) { const struct hdmi_platform_config *config = hdmi->config; - return gpio_get_value(config->hpd_gpio) ? + struct hdmi_gpio_data hpd_gpio = config->gpios[HPD_GPIO_INDEX]; + + return gpio_get_value(hpd_gpio.num) ? connector_status_connected : connector_status_disconnected; } @@ -358,9 +301,18 @@ static enum drm_connector_status hdmi_connector_detect( { struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector); struct hdmi *hdmi = hdmi_connector->hdmi; + const struct hdmi_platform_config *config = hdmi->config; + struct hdmi_gpio_data hpd_gpio = config->gpios[HPD_GPIO_INDEX]; enum drm_connector_status stat_gpio, stat_reg; int retry = 20; + /* + * some platforms may not have hpd gpio. Rely only on the status + * provided by REG_HDMI_HPD_INT_STATUS in this case. + */ + if (hpd_gpio.num == -1) + return detect_reg(hdmi); + do { stat_gpio = detect_gpio(hdmi); stat_reg = detect_reg(hdmi); @@ -395,7 +347,7 @@ static void hdmi_connector_destroy(struct drm_connector *connector) kfree(hdmi_connector); } -static int hdmi_connector_get_modes(struct drm_connector *connector) +static int msm_hdmi_connector_get_modes(struct drm_connector *connector) { struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector); struct hdmi *hdmi = hdmi_connector->hdmi; @@ -421,7 +373,7 @@ static int hdmi_connector_get_modes(struct drm_connector *connector) return ret; } -static int hdmi_connector_mode_valid(struct drm_connector *connector, +static int msm_hdmi_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector); @@ -451,7 +403,7 @@ static int hdmi_connector_mode_valid(struct drm_connector *connector, } static struct drm_encoder * -hdmi_connector_best_encoder(struct drm_connector *connector) +msm_hdmi_connector_best_encoder(struct drm_connector *connector) { struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector); return hdmi_connector->hdmi->encoder; @@ -467,14 +419,14 @@ static const struct drm_connector_funcs hdmi_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = { - .get_modes = hdmi_connector_get_modes, - .mode_valid = hdmi_connector_mode_valid, - .best_encoder = hdmi_connector_best_encoder, +static const struct drm_connector_helper_funcs msm_hdmi_connector_helper_funcs = { + .get_modes = msm_hdmi_connector_get_modes, + .mode_valid = msm_hdmi_connector_mode_valid, + .best_encoder = msm_hdmi_connector_best_encoder, }; /* initialize connector */ -struct drm_connector *hdmi_connector_init(struct hdmi *hdmi) +struct drm_connector *msm_hdmi_connector_init(struct hdmi *hdmi) { struct drm_connector *connector = NULL; struct hdmi_connector *hdmi_connector; @@ -487,13 +439,13 @@ struct drm_connector *hdmi_connector_init(struct hdmi *hdmi) } hdmi_connector->hdmi = hdmi; - INIT_WORK(&hdmi_connector->hpd_work, hotplug_work); + INIT_WORK(&hdmi_connector->hpd_work, msm_hdmi_hotplug_work); connector = &hdmi_connector->base; drm_connector_init(hdmi->dev, connector, &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); - drm_connector_helper_add(connector, &hdmi_connector_helper_funcs); + drm_connector_helper_add(connector, &msm_hdmi_connector_helper_funcs); connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c index 1dc9c34eb0df..0baaaaabd002 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c @@ -84,7 +84,7 @@ struct hdmi_hdcp_ctrl { bool max_dev_exceeded; }; -static int hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset, +static int msm_hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset, u8 *data, u16 data_len) { int rc; @@ -122,7 +122,7 @@ retry: #define HDCP_DDC_WRITE_MAX_BYTE_NUM 32 -static int hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset, +static int msm_hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset, u8 *data, u16 data_len) { int rc; @@ -162,7 +162,7 @@ retry: return rc; } -static int hdmi_hdcp_scm_wr(struct hdmi_hdcp_ctrl *hdcp_ctrl, u32 *preg, +static int msm_hdmi_hdcp_scm_wr(struct hdmi_hdcp_ctrl *hdcp_ctrl, u32 *preg, u32 *pdata, u32 count) { struct hdmi *hdmi = hdcp_ctrl->hdmi; @@ -202,7 +202,7 @@ static int hdmi_hdcp_scm_wr(struct hdmi_hdcp_ctrl *hdcp_ctrl, u32 *preg, return ret; } -void hdmi_hdcp_irq(struct hdmi_hdcp_ctrl *hdcp_ctrl) +void msm_hdmi_hdcp_irq(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; u32 reg_val, hdcp_int_status; @@ -247,7 +247,7 @@ void hdmi_hdcp_irq(struct hdmi_hdcp_ctrl *hdcp_ctrl) } } -static int hdmi_hdcp_msleep(struct hdmi_hdcp_ctrl *hdcp_ctrl, u32 ms, u32 ev) +static int msm_hdmi_hdcp_msleep(struct hdmi_hdcp_ctrl *hdcp_ctrl, u32 ms, u32 ev) { int rc; @@ -264,7 +264,7 @@ static int hdmi_hdcp_msleep(struct hdmi_hdcp_ctrl *hdcp_ctrl, u32 ms, u32 ev) return 0; } -static int hdmi_hdcp_read_validate_aksv(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_read_validate_aksv(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; @@ -287,7 +287,7 @@ static int hdmi_hdcp_read_validate_aksv(struct hdmi_hdcp_ctrl *hdcp_ctrl) return 0; } -static int reset_hdcp_ddc_failures(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_reset_hdcp_ddc_failures(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; u32 reg_val, failure, nack0; @@ -337,7 +337,7 @@ static int reset_hdcp_ddc_failures(struct hdmi_hdcp_ctrl *hdcp_ctrl) reg_val |= HDMI_DDC_CTRL_SW_STATUS_RESET; hdmi_write(hdmi, REG_HDMI_DDC_CTRL, reg_val); - rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); reg_val = hdmi_read(hdmi, REG_HDMI_DDC_CTRL); reg_val &= ~HDMI_DDC_CTRL_SW_STATUS_RESET; @@ -350,7 +350,7 @@ static int reset_hdcp_ddc_failures(struct hdmi_hdcp_ctrl *hdcp_ctrl) /* If previous msleep is aborted, skip this msleep */ if (!rc) - rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); reg_val = hdmi_read(hdmi, REG_HDMI_DDC_CTRL); reg_val &= ~HDMI_DDC_CTRL_SOFT_RESET; @@ -362,7 +362,7 @@ static int reset_hdcp_ddc_failures(struct hdmi_hdcp_ctrl *hdcp_ctrl) return rc; } -static int hdmi_hdcp_hw_ddc_clean(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_hw_ddc_clean(struct hdmi_hdcp_ctrl *hdcp_ctrl) { int rc; u32 hdcp_ddc_status, ddc_hw_status; @@ -394,7 +394,7 @@ static int hdmi_hdcp_hw_ddc_clean(struct hdmi_hdcp_ctrl *hdcp_ctrl) return -ETIMEDOUT; } - rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); if (rc) return rc; } while (1); @@ -402,7 +402,7 @@ static int hdmi_hdcp_hw_ddc_clean(struct hdmi_hdcp_ctrl *hdcp_ctrl) return 0; } -static void hdmi_hdcp_reauth_work(struct work_struct *work) +static void msm_hdmi_hdcp_reauth_work(struct work_struct *work) { struct hdmi_hdcp_ctrl *hdcp_ctrl = container_of(work, struct hdmi_hdcp_ctrl, hdcp_reauth_work); @@ -430,7 +430,7 @@ static void hdmi_hdcp_reauth_work(struct work_struct *work) HDMI_HDCP_RESET_LINK0_DEAUTHENTICATE); /* Wait to be clean on DDC HW engine */ - if (hdmi_hdcp_hw_ddc_clean(hdcp_ctrl)) { + if (msm_hdmi_hdcp_hw_ddc_clean(hdcp_ctrl)) { pr_info("%s: reauth work aborted\n", __func__); return; } @@ -461,7 +461,7 @@ static void hdmi_hdcp_reauth_work(struct work_struct *work) queue_work(hdmi->workq, &hdcp_ctrl->hdcp_auth_work); } -static int hdmi_hdcp_auth_prepare(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_auth_prepare(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; u32 link0_status; @@ -470,7 +470,7 @@ static int hdmi_hdcp_auth_prepare(struct hdmi_hdcp_ctrl *hdcp_ctrl) int rc; if (!hdcp_ctrl->aksv_valid) { - rc = hdmi_hdcp_read_validate_aksv(hdcp_ctrl); + rc = msm_hdmi_hdcp_read_validate_aksv(hdcp_ctrl); if (rc) { pr_err("%s: ASKV validation failed\n", __func__); hdcp_ctrl->hdcp_state = HDCP_STATE_NO_AKSV; @@ -538,12 +538,12 @@ static int hdmi_hdcp_auth_prepare(struct hdmi_hdcp_ctrl *hdcp_ctrl) DBG("An not ready after enabling HDCP"); /* Clear any DDC failures from previous tries before enable HDCP*/ - rc = reset_hdcp_ddc_failures(hdcp_ctrl); + rc = msm_reset_hdcp_ddc_failures(hdcp_ctrl); return rc; } -static void hdmi_hdcp_auth_fail(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static void msm_hdmi_hdcp_auth_fail(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; u32 reg_val; @@ -561,7 +561,7 @@ static void hdmi_hdcp_auth_fail(struct hdmi_hdcp_ctrl *hdcp_ctrl) queue_work(hdmi->workq, &hdcp_ctrl->hdcp_reauth_work); } -static void hdmi_hdcp_auth_done(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static void msm_hdmi_hdcp_auth_done(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; u32 reg_val; @@ -596,7 +596,7 @@ static void hdmi_hdcp_auth_done(struct hdmi_hdcp_ctrl *hdcp_ctrl) * Write An and AKSV to sink * Read BKSV from sink and write into HDCP engine */ -static int hdmi_hdcp_wait_key_an_ready(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_wait_key_an_ready(struct hdmi_hdcp_ctrl *hdcp_ctrl) { int rc; struct hdmi *hdmi = hdcp_ctrl->hdmi; @@ -621,7 +621,7 @@ static int hdmi_hdcp_wait_key_an_ready(struct hdmi_hdcp_ctrl *hdcp_ctrl) return -ETIMEDOUT; } - rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); if (rc) return rc; } while (1); @@ -643,7 +643,7 @@ static int hdmi_hdcp_wait_key_an_ready(struct hdmi_hdcp_ctrl *hdcp_ctrl) return -ETIMEDOUT; } - rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); if (rc) return rc; } while (1); @@ -651,7 +651,7 @@ static int hdmi_hdcp_wait_key_an_ready(struct hdmi_hdcp_ctrl *hdcp_ctrl) return 0; } -static int hdmi_hdcp_send_aksv_an(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_send_aksv_an(struct hdmi_hdcp_ctrl *hdcp_ctrl) { int rc = 0; struct hdmi *hdmi = hdcp_ctrl->hdmi; @@ -676,7 +676,7 @@ static int hdmi_hdcp_send_aksv_an(struct hdmi_hdcp_ctrl *hdcp_ctrl) aksv[4] = link0_aksv_1 & 0xFF; /* Write An to offset 0x18 */ - rc = hdmi_ddc_write(hdmi, HDCP_PORT_ADDR, 0x18, (u8 *)link0_an, + rc = msm_hdmi_ddc_write(hdmi, HDCP_PORT_ADDR, 0x18, (u8 *)link0_an, (u16)sizeof(link0_an)); if (rc) { pr_err("%s:An write failed\n", __func__); @@ -685,7 +685,7 @@ static int hdmi_hdcp_send_aksv_an(struct hdmi_hdcp_ctrl *hdcp_ctrl) DBG("Link0-An=%08x%08x", link0_an[0], link0_an[1]); /* Write AKSV to offset 0x10 */ - rc = hdmi_ddc_write(hdmi, HDCP_PORT_ADDR, 0x10, aksv, 5); + rc = msm_hdmi_ddc_write(hdmi, HDCP_PORT_ADDR, 0x10, aksv, 5); if (rc) { pr_err("%s:AKSV write failed\n", __func__); return rc; @@ -695,7 +695,7 @@ static int hdmi_hdcp_send_aksv_an(struct hdmi_hdcp_ctrl *hdcp_ctrl) return 0; } -static int hdmi_hdcp_recv_bksv(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_recv_bksv(struct hdmi_hdcp_ctrl *hdcp_ctrl) { int rc = 0; struct hdmi *hdmi = hdcp_ctrl->hdmi; @@ -703,7 +703,7 @@ static int hdmi_hdcp_recv_bksv(struct hdmi_hdcp_ctrl *hdcp_ctrl) u32 reg[2], data[2]; /* Read BKSV at offset 0x00 */ - rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x00, bksv, 5); + rc = msm_hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x00, bksv, 5); if (rc) { pr_err("%s:BKSV read failed\n", __func__); return rc; @@ -728,19 +728,19 @@ static int hdmi_hdcp_recv_bksv(struct hdmi_hdcp_ctrl *hdcp_ctrl) data[0] = hdcp_ctrl->bksv_lsb; reg[1] = REG_HDMI_HDCP_RCVPORT_DATA1; data[1] = hdcp_ctrl->bksv_msb; - rc = hdmi_hdcp_scm_wr(hdcp_ctrl, reg, data, 2); + rc = msm_hdmi_hdcp_scm_wr(hdcp_ctrl, reg, data, 2); return rc; } -static int hdmi_hdcp_recv_bcaps(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_recv_bcaps(struct hdmi_hdcp_ctrl *hdcp_ctrl) { int rc = 0; struct hdmi *hdmi = hdcp_ctrl->hdmi; u32 reg, data; u8 bcaps; - rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x40, &bcaps, 1); + rc = msm_hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x40, &bcaps, 1); if (rc) { pr_err("%s:BCAPS read failed\n", __func__); return rc; @@ -753,26 +753,26 @@ static int hdmi_hdcp_recv_bcaps(struct hdmi_hdcp_ctrl *hdcp_ctrl) /* Write BCAPS to the hardware */ reg = REG_HDMI_HDCP_RCVPORT_DATA12; data = (u32)bcaps; - rc = hdmi_hdcp_scm_wr(hdcp_ctrl, ®, &data, 1); + rc = msm_hdmi_hdcp_scm_wr(hdcp_ctrl, ®, &data, 1); return rc; } -static int hdmi_hdcp_auth_part1_key_exchange(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_auth_part1_key_exchange(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; unsigned long flags; int rc; /* Wait for AKSV key and An ready */ - rc = hdmi_hdcp_wait_key_an_ready(hdcp_ctrl); + rc = msm_hdmi_hdcp_wait_key_an_ready(hdcp_ctrl); if (rc) { pr_err("%s: wait key and an ready failed\n", __func__); return rc; }; /* Read BCAPS and send to HDCP engine */ - rc = hdmi_hdcp_recv_bcaps(hdcp_ctrl); + rc = msm_hdmi_hdcp_recv_bcaps(hdcp_ctrl); if (rc) { pr_err("%s: read bcaps error, abort\n", __func__); return rc; @@ -785,14 +785,14 @@ static int hdmi_hdcp_auth_part1_key_exchange(struct hdmi_hdcp_ctrl *hdcp_ctrl) hdmi_write(hdmi, REG_HDMI_HDCP_RCVPORT_DATA4, 0); /* Send AKSV and An to sink */ - rc = hdmi_hdcp_send_aksv_an(hdcp_ctrl); + rc = msm_hdmi_hdcp_send_aksv_an(hdcp_ctrl); if (rc) { pr_err("%s:An/Aksv write failed\n", __func__); return rc; } /* Read BKSV and send to HDCP engine*/ - rc = hdmi_hdcp_recv_bksv(hdcp_ctrl); + rc = msm_hdmi_hdcp_recv_bksv(hdcp_ctrl); if (rc) { pr_err("%s:BKSV Process failed\n", __func__); return rc; @@ -812,7 +812,7 @@ static int hdmi_hdcp_auth_part1_key_exchange(struct hdmi_hdcp_ctrl *hdcp_ctrl) } /* read R0' from sink and pass it to HDCP engine */ -static int hdmi_hdcp_auth_part1_recv_r0(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_auth_part1_recv_r0(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; int rc = 0; @@ -822,12 +822,12 @@ static int hdmi_hdcp_auth_part1_recv_r0(struct hdmi_hdcp_ctrl *hdcp_ctrl) * HDCP Compliance Test case 1A-01: * Wait here at least 100ms before reading R0' */ - rc = hdmi_hdcp_msleep(hdcp_ctrl, 125, AUTH_ABORT_EV); + rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 125, AUTH_ABORT_EV); if (rc) return rc; /* Read R0' at offset 0x08 */ - rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x08, buf, 2); + rc = msm_hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x08, buf, 2); if (rc) { pr_err("%s:R0' read failed\n", __func__); return rc; @@ -842,14 +842,14 @@ static int hdmi_hdcp_auth_part1_recv_r0(struct hdmi_hdcp_ctrl *hdcp_ctrl) } /* Wait for authenticating result: R0/R0' are matched or not */ -static int hdmi_hdcp_auth_part1_verify_r0(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_auth_part1_verify_r0(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; u32 link0_status; int rc; /* wait for hdcp irq, 10 sec should be long enough */ - rc = hdmi_hdcp_msleep(hdcp_ctrl, 10000, AUTH_RESULT_RDY_EV); + rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 10000, AUTH_RESULT_RDY_EV); if (!rc) { pr_err("%s: Wait Auth IRQ timeout\n", __func__); return -ETIMEDOUT; @@ -869,7 +869,7 @@ static int hdmi_hdcp_auth_part1_verify_r0(struct hdmi_hdcp_ctrl *hdcp_ctrl) return 0; } -static int hdmi_hdcp_recv_check_bstatus(struct hdmi_hdcp_ctrl *hdcp_ctrl, +static int msm_hdmi_hdcp_recv_check_bstatus(struct hdmi_hdcp_ctrl *hdcp_ctrl, u16 *pbstatus) { int rc; @@ -880,7 +880,7 @@ static int hdmi_hdcp_recv_check_bstatus(struct hdmi_hdcp_ctrl *hdcp_ctrl, u8 buf[2]; /* Read BSTATUS at offset 0x41 */ - rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x41, buf, 2); + rc = msm_hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x41, buf, 2); if (rc) { pr_err("%s: BSTATUS read failed\n", __func__); goto error; @@ -936,7 +936,7 @@ error: return rc; } -static int hdmi_hdcp_auth_part2_wait_ksv_fifo_ready( +static int msm_hdmi_hdcp_auth_part2_wait_ksv_fifo_ready( struct hdmi_hdcp_ctrl *hdcp_ctrl) { int rc; @@ -953,7 +953,7 @@ static int hdmi_hdcp_auth_part2_wait_ksv_fifo_ready( timeout_count = 100; do { /* Read BCAPS at offset 0x40 */ - rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x40, &bcaps, 1); + rc = msm_hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x40, &bcaps, 1); if (rc) { pr_err("%s: BCAPS read failed\n", __func__); return rc; @@ -968,12 +968,12 @@ static int hdmi_hdcp_auth_part2_wait_ksv_fifo_ready( return -ETIMEDOUT; } - rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); if (rc) return rc; } while (1); - rc = hdmi_hdcp_recv_check_bstatus(hdcp_ctrl, &bstatus); + rc = msm_hdmi_hdcp_recv_check_bstatus(hdcp_ctrl, &bstatus); if (rc) { pr_err("%s: bstatus error\n", __func__); return rc; @@ -982,7 +982,7 @@ static int hdmi_hdcp_auth_part2_wait_ksv_fifo_ready( /* Write BSTATUS and BCAPS to HDCP registers */ reg = REG_HDMI_HDCP_RCVPORT_DATA12; data = bcaps | (bstatus << 8); - rc = hdmi_hdcp_scm_wr(hdcp_ctrl, ®, &data, 1); + rc = msm_hdmi_hdcp_scm_wr(hdcp_ctrl, ®, &data, 1); if (rc) { pr_err("%s: BSTATUS write failed\n", __func__); return rc; @@ -997,7 +997,7 @@ static int hdmi_hdcp_auth_part2_wait_ksv_fifo_ready( * transfer V' from sink to HDCP engine * reset SHA engine */ -static int hdmi_hdcp_transfer_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_transfer_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; int rc = 0; @@ -1016,7 +1016,7 @@ static int hdmi_hdcp_transfer_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl) for (i = 0; i < size; i++) { rd = ®_data[i]; - rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, + rc = msm_hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, rd->off, (u8 *)&data[i], (u16)sizeof(data[i])); if (rc) { pr_err("%s: Read %s failed\n", __func__, rd->name); @@ -1027,13 +1027,13 @@ static int hdmi_hdcp_transfer_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl) reg[i] = reg_data[i].reg_id; } - rc = hdmi_hdcp_scm_wr(hdcp_ctrl, reg, data, size); + rc = msm_hdmi_hdcp_scm_wr(hdcp_ctrl, reg, data, size); error: return rc; } -static int hdmi_hdcp_recv_ksv_fifo(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_recv_ksv_fifo(struct hdmi_hdcp_ctrl *hdcp_ctrl) { int rc; struct hdmi *hdmi = hdcp_ctrl->hdmi; @@ -1041,7 +1041,7 @@ static int hdmi_hdcp_recv_ksv_fifo(struct hdmi_hdcp_ctrl *hdcp_ctrl) ksv_bytes = 5 * hdcp_ctrl->dev_count; - rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x43, + rc = msm_hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x43, hdcp_ctrl->ksv_list, ksv_bytes); if (rc) pr_err("%s: KSV FIFO read failed\n", __func__); @@ -1049,7 +1049,7 @@ static int hdmi_hdcp_recv_ksv_fifo(struct hdmi_hdcp_ctrl *hdcp_ctrl) return rc; } -static int hdmi_hdcp_reset_sha_engine(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_reset_sha_engine(struct hdmi_hdcp_ctrl *hdcp_ctrl) { u32 reg[2], data[2]; u32 rc = 0; @@ -1059,12 +1059,12 @@ static int hdmi_hdcp_reset_sha_engine(struct hdmi_hdcp_ctrl *hdcp_ctrl) reg[1] = REG_HDMI_HDCP_SHA_CTRL; data[1] = HDCP_REG_DISABLE; - rc = hdmi_hdcp_scm_wr(hdcp_ctrl, reg, data, 2); + rc = msm_hdmi_hdcp_scm_wr(hdcp_ctrl, reg, data, 2); return rc; } -static int hdmi_hdcp_auth_part2_recv_ksv_fifo( +static int msm_hdmi_hdcp_auth_part2_recv_ksv_fifo( struct hdmi_hdcp_ctrl *hdcp_ctrl) { int rc; @@ -1081,7 +1081,7 @@ static int hdmi_hdcp_auth_part2_recv_ksv_fifo( */ timeout_count = 100; do { - rc = hdmi_hdcp_recv_ksv_fifo(hdcp_ctrl); + rc = msm_hdmi_hdcp_recv_ksv_fifo(hdcp_ctrl); if (!rc) break; @@ -1091,19 +1091,19 @@ static int hdmi_hdcp_auth_part2_recv_ksv_fifo( return -ETIMEDOUT; } - rc = hdmi_hdcp_msleep(hdcp_ctrl, 25, AUTH_ABORT_EV); + rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 25, AUTH_ABORT_EV); if (rc) return rc; } while (1); - rc = hdmi_hdcp_transfer_v_h(hdcp_ctrl); + rc = msm_hdmi_hdcp_transfer_v_h(hdcp_ctrl); if (rc) { pr_err("%s: transfer V failed\n", __func__); return rc; } /* reset SHA engine before write ksv fifo */ - rc = hdmi_hdcp_reset_sha_engine(hdcp_ctrl); + rc = msm_hdmi_hdcp_reset_sha_engine(hdcp_ctrl); if (rc) { pr_err("%s: fail to reset sha engine\n", __func__); return rc; @@ -1120,7 +1120,7 @@ static int hdmi_hdcp_auth_part2_recv_ksv_fifo( * If the last byte is written, we need to poll for * HDCP_SHA_COMP_DONE to wait until HW finish */ -static int hdmi_hdcp_write_ksv_fifo(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_write_ksv_fifo(struct hdmi_hdcp_ctrl *hdcp_ctrl) { int i; struct hdmi *hdmi = hdcp_ctrl->hdmi; @@ -1169,7 +1169,7 @@ static int hdmi_hdcp_write_ksv_fifo(struct hdmi_hdcp_ctrl *hdcp_ctrl) reg = REG_HDMI_HDCP_SHA_DATA; data = reg_val; - rc = hdmi_hdcp_scm_wr(hdcp_ctrl, ®, &data, 1); + rc = msm_hdmi_hdcp_scm_wr(hdcp_ctrl, ®, &data, 1); if (rc) return rc; @@ -1184,7 +1184,7 @@ static int hdmi_hdcp_write_ksv_fifo(struct hdmi_hdcp_ctrl *hdcp_ctrl) } /* write ksv fifo into HDCP engine */ -static int hdmi_hdcp_auth_part2_write_ksv_fifo( +static int msm_hdmi_hdcp_auth_part2_write_ksv_fifo( struct hdmi_hdcp_ctrl *hdcp_ctrl) { int rc; @@ -1193,7 +1193,7 @@ static int hdmi_hdcp_auth_part2_write_ksv_fifo( hdcp_ctrl->ksv_fifo_w_index = 0; timeout_count = 100; do { - rc = hdmi_hdcp_write_ksv_fifo(hdcp_ctrl); + rc = msm_hdmi_hdcp_write_ksv_fifo(hdcp_ctrl); if (!rc) break; @@ -1206,7 +1206,7 @@ static int hdmi_hdcp_auth_part2_write_ksv_fifo( return -ETIMEDOUT; } - rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); if (rc) return rc; } while (1); @@ -1214,7 +1214,7 @@ static int hdmi_hdcp_auth_part2_write_ksv_fifo( return 0; } -static int hdmi_hdcp_auth_part2_check_v_match(struct hdmi_hdcp_ctrl *hdcp_ctrl) +static int msm_hdmi_hdcp_auth_part2_check_v_match(struct hdmi_hdcp_ctrl *hdcp_ctrl) { int rc = 0; struct hdmi *hdmi = hdcp_ctrl->hdmi; @@ -1232,7 +1232,7 @@ static int hdmi_hdcp_auth_part2_check_v_match(struct hdmi_hdcp_ctrl *hdcp_ctrl) return -ETIMEDOUT; } - rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); + rc = msm_hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV); if (rc) return rc; } while (1); @@ -1240,32 +1240,32 @@ static int hdmi_hdcp_auth_part2_check_v_match(struct hdmi_hdcp_ctrl *hdcp_ctrl) return 0; } -static void hdmi_hdcp_auth_work(struct work_struct *work) +static void msm_hdmi_hdcp_auth_work(struct work_struct *work) { struct hdmi_hdcp_ctrl *hdcp_ctrl = container_of(work, struct hdmi_hdcp_ctrl, hdcp_auth_work); int rc; - rc = hdmi_hdcp_auth_prepare(hdcp_ctrl); + rc = msm_hdmi_hdcp_auth_prepare(hdcp_ctrl); if (rc) { pr_err("%s: auth prepare failed %d\n", __func__, rc); goto end; } /* HDCP PartI */ - rc = hdmi_hdcp_auth_part1_key_exchange(hdcp_ctrl); + rc = msm_hdmi_hdcp_auth_part1_key_exchange(hdcp_ctrl); if (rc) { pr_err("%s: key exchange failed %d\n", __func__, rc); goto end; } - rc = hdmi_hdcp_auth_part1_recv_r0(hdcp_ctrl); + rc = msm_hdmi_hdcp_auth_part1_recv_r0(hdcp_ctrl); if (rc) { pr_err("%s: receive r0 failed %d\n", __func__, rc); goto end; } - rc = hdmi_hdcp_auth_part1_verify_r0(hdcp_ctrl); + rc = msm_hdmi_hdcp_auth_part1_verify_r0(hdcp_ctrl); if (rc) { pr_err("%s: verify r0 failed %d\n", __func__, rc); goto end; @@ -1275,25 +1275,25 @@ static void hdmi_hdcp_auth_work(struct work_struct *work) goto end; /* HDCP PartII */ - rc = hdmi_hdcp_auth_part2_wait_ksv_fifo_ready(hdcp_ctrl); + rc = msm_hdmi_hdcp_auth_part2_wait_ksv_fifo_ready(hdcp_ctrl); if (rc) { pr_err("%s: wait ksv fifo ready failed %d\n", __func__, rc); goto end; } - rc = hdmi_hdcp_auth_part2_recv_ksv_fifo(hdcp_ctrl); + rc = msm_hdmi_hdcp_auth_part2_recv_ksv_fifo(hdcp_ctrl); if (rc) { pr_err("%s: recv ksv fifo failed %d\n", __func__, rc); goto end; } - rc = hdmi_hdcp_auth_part2_write_ksv_fifo(hdcp_ctrl); + rc = msm_hdmi_hdcp_auth_part2_write_ksv_fifo(hdcp_ctrl); if (rc) { pr_err("%s: write ksv fifo failed %d\n", __func__, rc); goto end; } - rc = hdmi_hdcp_auth_part2_check_v_match(hdcp_ctrl); + rc = msm_hdmi_hdcp_auth_part2_check_v_match(hdcp_ctrl); if (rc) pr_err("%s: check v match failed %d\n", __func__, rc); @@ -1304,13 +1304,13 @@ end: pr_info("%s: hdcp is not supported\n", __func__); } else if (rc) { pr_err("%s: hdcp authentication failed\n", __func__); - hdmi_hdcp_auth_fail(hdcp_ctrl); + msm_hdmi_hdcp_auth_fail(hdcp_ctrl); } else { - hdmi_hdcp_auth_done(hdcp_ctrl); + msm_hdmi_hdcp_auth_done(hdcp_ctrl); } } -void hdmi_hdcp_on(struct hdmi_hdcp_ctrl *hdcp_ctrl) +void msm_hdmi_hdcp_on(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; u32 reg_val; @@ -1335,7 +1335,7 @@ void hdmi_hdcp_on(struct hdmi_hdcp_ctrl *hdcp_ctrl) queue_work(hdmi->workq, &hdcp_ctrl->hdcp_auth_work); } -void hdmi_hdcp_off(struct hdmi_hdcp_ctrl *hdcp_ctrl) +void msm_hdmi_hdcp_off(struct hdmi_hdcp_ctrl *hdcp_ctrl) { struct hdmi *hdmi = hdcp_ctrl->hdmi; unsigned long flags; @@ -1399,7 +1399,7 @@ void hdmi_hdcp_off(struct hdmi_hdcp_ctrl *hdcp_ctrl) DBG("HDCP: Off"); } -struct hdmi_hdcp_ctrl *hdmi_hdcp_init(struct hdmi *hdmi) +struct hdmi_hdcp_ctrl *msm_hdmi_hdcp_init(struct hdmi *hdmi) { struct hdmi_hdcp_ctrl *hdcp_ctrl = NULL; @@ -1413,8 +1413,8 @@ struct hdmi_hdcp_ctrl *hdmi_hdcp_init(struct hdmi *hdmi) if (!hdcp_ctrl) return ERR_PTR(-ENOMEM); - INIT_WORK(&hdcp_ctrl->hdcp_auth_work, hdmi_hdcp_auth_work); - INIT_WORK(&hdcp_ctrl->hdcp_reauth_work, hdmi_hdcp_reauth_work); + INIT_WORK(&hdcp_ctrl->hdcp_auth_work, msm_hdmi_hdcp_auth_work); + INIT_WORK(&hdcp_ctrl->hdcp_reauth_work, msm_hdmi_hdcp_reauth_work); init_waitqueue_head(&hdcp_ctrl->auth_event_queue); hdcp_ctrl->hdmi = hdmi; hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE; @@ -1428,7 +1428,7 @@ struct hdmi_hdcp_ctrl *hdmi_hdcp_init(struct hdmi *hdmi) return hdcp_ctrl; } -void hdmi_hdcp_destroy(struct hdmi *hdmi) +void msm_hdmi_hdcp_destroy(struct hdmi *hdmi) { if (hdmi && hdmi->hdcp_ctrl) { kfree(hdmi->hdcp_ctrl); diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c index f4ab7f70fed1..de9007e72f4e 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_i2c.c @@ -97,7 +97,7 @@ static bool sw_done(struct hdmi_i2c_adapter *hdmi_i2c) return hdmi_i2c->sw_done; } -static int hdmi_i2c_xfer(struct i2c_adapter *i2c, +static int msm_hdmi_i2c_xfer(struct i2c_adapter *i2c, struct i2c_msg *msgs, int num) { struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(i2c); @@ -216,17 +216,17 @@ static int hdmi_i2c_xfer(struct i2c_adapter *i2c, return i; } -static u32 hdmi_i2c_func(struct i2c_adapter *adapter) +static u32 msm_hdmi_i2c_func(struct i2c_adapter *adapter) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; } -static const struct i2c_algorithm hdmi_i2c_algorithm = { - .master_xfer = hdmi_i2c_xfer, - .functionality = hdmi_i2c_func, +static const struct i2c_algorithm msm_hdmi_i2c_algorithm = { + .master_xfer = msm_hdmi_i2c_xfer, + .functionality = msm_hdmi_i2c_func, }; -void hdmi_i2c_irq(struct i2c_adapter *i2c) +void msm_hdmi_i2c_irq(struct i2c_adapter *i2c) { struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(i2c); @@ -234,14 +234,14 @@ void hdmi_i2c_irq(struct i2c_adapter *i2c) wake_up_all(&hdmi_i2c->ddc_event); } -void hdmi_i2c_destroy(struct i2c_adapter *i2c) +void msm_hdmi_i2c_destroy(struct i2c_adapter *i2c) { struct hdmi_i2c_adapter *hdmi_i2c = to_hdmi_i2c_adapter(i2c); i2c_del_adapter(i2c); kfree(hdmi_i2c); } -struct i2c_adapter *hdmi_i2c_init(struct hdmi *hdmi) +struct i2c_adapter *msm_hdmi_i2c_init(struct hdmi *hdmi) { struct drm_device *dev = hdmi->dev; struct hdmi_i2c_adapter *hdmi_i2c; @@ -264,7 +264,7 @@ struct i2c_adapter *hdmi_i2c_init(struct hdmi *hdmi) i2c->class = I2C_CLASS_DDC; snprintf(i2c->name, sizeof(i2c->name), "msm hdmi i2c"); i2c->dev.parent = &hdmi->pdev->dev; - i2c->algo = &hdmi_i2c_algorithm; + i2c->algo = &msm_hdmi_i2c_algorithm; ret = i2c_add_adapter(i2c); if (ret) { @@ -276,6 +276,6 @@ struct i2c_adapter *hdmi_i2c_init(struct hdmi *hdmi) fail: if (i2c) - hdmi_i2c_destroy(i2c); + msm_hdmi_i2c_destroy(i2c); return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c new file mode 100644 index 000000000000..534ce5b49781 --- /dev/null +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/of_device.h> + +#include "hdmi.h" + +static int msm_hdmi_phy_resource_init(struct hdmi_phy *phy) +{ + struct hdmi_phy_cfg *cfg = phy->cfg; + struct device *dev = &phy->pdev->dev; + int i, ret; + + phy->regs = devm_kzalloc(dev, sizeof(phy->regs[0]) * cfg->num_regs, + GFP_KERNEL); + if (!phy->regs) + return -ENOMEM; + + phy->clks = devm_kzalloc(dev, sizeof(phy->clks[0]) * cfg->num_clks, + GFP_KERNEL); + if (!phy->clks) + return -ENOMEM; + + for (i = 0; i < cfg->num_regs; i++) { + struct regulator *reg; + + reg = devm_regulator_get(dev, cfg->reg_names[i]); + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + dev_err(dev, "failed to get phy regulator: %s (%d)\n", + cfg->reg_names[i], ret); + return ret; + } + + phy->regs[i] = reg; + } + + for (i = 0; i < cfg->num_clks; i++) { + struct clk *clk; + + clk = devm_clk_get(dev, cfg->clk_names[i]); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(dev, "failed to get phy clock: %s (%d)\n", + cfg->clk_names[i], ret); + return ret; + } + + phy->clks[i] = clk; + } + + return 0; +} + +int msm_hdmi_phy_resource_enable(struct hdmi_phy *phy) +{ + struct hdmi_phy_cfg *cfg = phy->cfg; + struct device *dev = &phy->pdev->dev; + int i, ret = 0; + + pm_runtime_get_sync(dev); + + for (i = 0; i < cfg->num_regs; i++) { + ret = regulator_enable(phy->regs[i]); + if (ret) + dev_err(dev, "failed to enable regulator: %s (%d)\n", + cfg->reg_names[i], ret); + } + + for (i = 0; i < cfg->num_clks; i++) { + ret = clk_prepare_enable(phy->clks[i]); + if (ret) + dev_err(dev, "failed to enable clock: %s (%d)\n", + cfg->clk_names[i], ret); + } + + return ret; +} + +void msm_hdmi_phy_resource_disable(struct hdmi_phy *phy) +{ + struct hdmi_phy_cfg *cfg = phy->cfg; + struct device *dev = &phy->pdev->dev; + int i; + + for (i = cfg->num_clks - 1; i >= 0; i--) + clk_disable_unprepare(phy->clks[i]); + + for (i = cfg->num_regs - 1; i >= 0; i--) + regulator_disable(phy->regs[i]); + + pm_runtime_put_sync(dev); +} + +void msm_hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long int pixclock) +{ + if (!phy || !phy->cfg->powerup) + return; + + phy->cfg->powerup(phy, pixclock); +} + +void msm_hdmi_phy_powerdown(struct hdmi_phy *phy) +{ + if (!phy || !phy->cfg->powerdown) + return; + + phy->cfg->powerdown(phy); +} + +static int msm_hdmi_phy_pll_init(struct platform_device *pdev, + enum hdmi_phy_type type) +{ + int ret; + + switch (type) { + case MSM_HDMI_PHY_8960: + ret = msm_hdmi_pll_8960_init(pdev); + break; + case MSM_HDMI_PHY_8996: + ret = msm_hdmi_pll_8996_init(pdev); + break; + /* + * we don't have PLL support for these, don't report an error for now + */ + case MSM_HDMI_PHY_8x60: + case MSM_HDMI_PHY_8x74: + default: + ret = 0; + break; + } + + return ret; +} + +static int msm_hdmi_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct hdmi_phy *phy; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENODEV; + + phy->cfg = (struct hdmi_phy_cfg *)of_device_get_match_data(dev); + if (!phy->cfg) + return -ENODEV; + + phy->mmio = msm_ioremap(pdev, "hdmi_phy", "HDMI_PHY"); + if (IS_ERR(phy->mmio)) { + dev_err(dev, "%s: failed to map phy base\n", __func__); + return -ENOMEM; + } + + phy->pdev = pdev; + + ret = msm_hdmi_phy_resource_init(phy); + if (ret) + return ret; + + pm_runtime_enable(&pdev->dev); + + ret = msm_hdmi_phy_resource_enable(phy); + if (ret) + return ret; + + ret = msm_hdmi_phy_pll_init(pdev, phy->cfg->type); + if (ret) { + dev_err(dev, "couldn't init PLL\n"); + msm_hdmi_phy_resource_disable(phy); + return ret; + } + + msm_hdmi_phy_resource_disable(phy); + + platform_set_drvdata(pdev, phy); + + return 0; +} + +static int msm_hdmi_phy_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id msm_hdmi_phy_dt_match[] = { + { .compatible = "qcom,hdmi-phy-8660", + .data = &msm_hdmi_phy_8x60_cfg }, + { .compatible = "qcom,hdmi-phy-8960", + .data = &msm_hdmi_phy_8960_cfg }, + { .compatible = "qcom,hdmi-phy-8974", + .data = &msm_hdmi_phy_8x74_cfg }, + { .compatible = "qcom,hdmi-phy-8084", + .data = &msm_hdmi_phy_8x74_cfg }, + { .compatible = "qcom,hdmi-phy-8996", + .data = &msm_hdmi_phy_8996_cfg }, + {} +}; + +static struct platform_driver msm_hdmi_phy_platform_driver = { + .probe = msm_hdmi_phy_probe, + .remove = msm_hdmi_phy_remove, + .driver = { + .name = "msm_hdmi_phy", + .of_match_table = msm_hdmi_phy_dt_match, + }, +}; + +void __init msm_hdmi_phy_driver_register(void) +{ + platform_driver_register(&msm_hdmi_phy_platform_driver); +} + +void __exit msm_hdmi_phy_driver_unregister(void) +{ + platform_driver_unregister(&msm_hdmi_phy_platform_driver); +} diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c index 3a01cb5051e2..e6ee6b745ab7 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c @@ -15,495 +15,48 @@ * this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifdef CONFIG_COMMON_CLK -#include <linux/clk.h> -#include <linux/clk-provider.h> -#endif - #include "hdmi.h" -struct hdmi_phy_8960 { - struct hdmi_phy base; - struct hdmi *hdmi; -#ifdef CONFIG_COMMON_CLK - struct clk_hw pll_hw; - struct clk *pll; - unsigned long pixclk; -#endif -}; -#define to_hdmi_phy_8960(x) container_of(x, struct hdmi_phy_8960, base) - -#ifdef CONFIG_COMMON_CLK -#define clk_to_phy(x) container_of(x, struct hdmi_phy_8960, pll_hw) - -/* - * HDMI PLL: - * - * To get the parent clock setup properly, we need to plug in hdmi pll - * configuration into common-clock-framework. - */ - -struct pll_rate { - unsigned long rate; - struct { - uint32_t val; - uint32_t reg; - } conf[32]; -}; - -/* NOTE: keep sorted highest freq to lowest: */ -static const struct pll_rate freqtbl[] = { - { 154000000, { - { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x0d, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x4d, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0x5e, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0x42, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - { 0, 0 } } - }, - /* 1080p60/1080p50 case */ - { 148500000, { - { 0x02, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, - { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG }, - { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG }, - { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, - { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3 }, - { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 }, - { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 }, - { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 }, - { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, - { 0, 0 } } - }, - { 108000000, { - { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x21, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x1c, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - { 0, 0 } } - }, - /* 720p60/720p50/1080i60/1080i50/1080p24/1080p30/1080p25 */ - { 74250000, { - { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, - { 0x12, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0, 0 } } - }, - { 74176000, { - { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0xe5, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x0c, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0x7d, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0xbc, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - { 0, 0 } } - }, - { 65000000, { - { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x8a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x0b, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x4b, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0x09, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - { 0, 0 } } - }, - /* 480p60/480i60 */ - { 27030000, { - { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, - { 0x38, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, - { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0xff, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x4e, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0xd7, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0x03, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, - { 0, 0 } } - }, - /* 576p50/576i50 */ - { 27000000, { - { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, - { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG }, - { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG }, - { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, - { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3 }, - { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 }, - { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 }, - { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 }, - { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, - { 0, 0 } } - }, - /* 640x480p60 */ - { 25200000, { - { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, - { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, - { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, - { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, - { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG }, - { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG }, - { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, - { 0x77, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, - { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, - { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, - { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 }, - { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 }, - { 0x20, REG_HDMI_8960_PHY_PLL_SSC_CFG3 }, - { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 }, - { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 }, - { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 }, - { 0xf4, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, - { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, - { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, - { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, - { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, - { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, - { 0, 0 } } - }, -}; - -static int hdmi_pll_enable(struct clk_hw *hw) -{ - struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw); - struct hdmi *hdmi = phy_8960->hdmi; - int timeout_count, pll_lock_retry = 10; - unsigned int val; - - DBG(""); - - /* Assert PLL S/W reset */ - hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d); - hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0, 0x10); - hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1, 0x1a); - - /* Wait for a short time before de-asserting - * to allow the hardware to complete its job. - * This much of delay should be fine for hardware - * to assert and de-assert. - */ - udelay(10); - - /* De-assert PLL S/W reset */ - hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d); - - val = hdmi_read(hdmi, REG_HDMI_8960_PHY_REG12); - val |= HDMI_8960_PHY_REG12_SW_RESET; - /* Assert PHY S/W reset */ - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val); - val &= ~HDMI_8960_PHY_REG12_SW_RESET; - /* Wait for a short time before de-asserting - to allow the hardware to complete its job. - This much of delay should be fine for hardware - to assert and de-assert. */ - udelay(10); - /* De-assert PHY S/W reset */ - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x3f); - - val = hdmi_read(hdmi, REG_HDMI_8960_PHY_REG12); - val |= HDMI_8960_PHY_REG12_PWRDN_B; - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val); - /* Wait 10 us for enabling global power for PHY */ - mb(); - udelay(10); - - val = hdmi_read(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B); - val |= HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B; - val &= ~HDMI_8960_PHY_PLL_PWRDN_B_PD_PLL; - hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B, val); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x80); - - timeout_count = 1000; - while (--pll_lock_retry > 0) { - - /* are we there yet? */ - val = hdmi_read(hdmi, REG_HDMI_8960_PHY_PLL_STATUS0); - if (val & HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK) - break; - - udelay(1); - - if (--timeout_count > 0) - continue; - - /* - * PLL has still not locked. - * Do a software reset and try again - * Assert PLL S/W reset first - */ - hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d); - udelay(10); - hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d); - - /* - * Wait for a short duration for the PLL calibration - * before checking if the PLL gets locked - */ - udelay(350); - - timeout_count = 1000; - } - - return 0; -} - -static void hdmi_pll_disable(struct clk_hw *hw) -{ - struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw); - struct hdmi *hdmi = phy_8960->hdmi; - unsigned int val; - - DBG(""); - - val = hdmi_read(hdmi, REG_HDMI_8960_PHY_REG12); - val &= ~HDMI_8960_PHY_REG12_PWRDN_B; - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val); - - val = hdmi_read(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B); - val |= HDMI_8960_PHY_REG12_SW_RESET; - val &= ~HDMI_8960_PHY_REG12_PWRDN_B; - hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B, val); - /* Make sure HDMI PHY/PLL are powered down */ - mb(); -} - -static const struct pll_rate *find_rate(unsigned long rate) -{ - int i; - for (i = 1; i < ARRAY_SIZE(freqtbl); i++) - if (rate > freqtbl[i].rate) - return &freqtbl[i-1]; - return &freqtbl[i-1]; -} - -static unsigned long hdmi_pll_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw); - return phy_8960->pixclk; -} - -static long hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) -{ - const struct pll_rate *pll_rate = find_rate(rate); - return pll_rate->rate; -} - -static int hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) -{ - struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw); - struct hdmi *hdmi = phy_8960->hdmi; - const struct pll_rate *pll_rate = find_rate(rate); - int i; - - DBG("rate=%lu", rate); - - for (i = 0; pll_rate->conf[i].reg; i++) - hdmi_write(hdmi, pll_rate->conf[i].reg, pll_rate->conf[i].val); - - phy_8960->pixclk = rate; - - return 0; -} - - -static const struct clk_ops hdmi_pll_ops = { - .enable = hdmi_pll_enable, - .disable = hdmi_pll_disable, - .recalc_rate = hdmi_pll_recalc_rate, - .round_rate = hdmi_pll_round_rate, - .set_rate = hdmi_pll_set_rate, -}; - -static const char *hdmi_pll_parents[] = { - "pxo", -}; - -static struct clk_init_data pll_init = { - .name = "hdmi_pll", - .ops = &hdmi_pll_ops, - .parent_names = hdmi_pll_parents, - .num_parents = ARRAY_SIZE(hdmi_pll_parents), -}; -#endif - -/* - * HDMI Phy: - */ - -static void hdmi_phy_8960_destroy(struct hdmi_phy *phy) -{ - struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy); - kfree(phy_8960); -} - static void hdmi_phy_8960_powerup(struct hdmi_phy *phy, - unsigned long int pixclock) + unsigned long int pixclock) { - struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy); - struct hdmi *hdmi = phy_8960->hdmi; - DBG("pixclock: %lu", pixclock); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x00); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG0, 0x1b); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG1, 0xf2); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG4, 0x00); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG5, 0x00); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG6, 0x00); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG7, 0x00); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG8, 0x00); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG9, 0x00); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG10, 0x00); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG11, 0x00); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG3, 0x20); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2, 0x00); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG0, 0x1b); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG1, 0xf2); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG4, 0x00); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG5, 0x00); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG6, 0x00); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG7, 0x00); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG8, 0x00); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG9, 0x00); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG10, 0x00); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG11, 0x00); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG3, 0x20); } static void hdmi_phy_8960_powerdown(struct hdmi_phy *phy) { - struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy); - struct hdmi *hdmi = phy_8960->hdmi; - DBG(""); - hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x7f); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2, 0x7f); } -static const struct hdmi_phy_funcs hdmi_phy_8960_funcs = { - .destroy = hdmi_phy_8960_destroy, - .powerup = hdmi_phy_8960_powerup, - .powerdown = hdmi_phy_8960_powerdown, +static const char * const hdmi_phy_8960_reg_names[] = { + "core-vdda", }; -struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi) -{ - struct hdmi_phy_8960 *phy_8960; - struct hdmi_phy *phy = NULL; - int ret; -#ifdef CONFIG_COMMON_CLK - int i; - - /* sanity check: */ - for (i = 0; i < (ARRAY_SIZE(freqtbl) - 1); i++) - if (WARN_ON(freqtbl[i].rate < freqtbl[i+1].rate)) - return ERR_PTR(-EINVAL); -#endif - - phy_8960 = kzalloc(sizeof(*phy_8960), GFP_KERNEL); - if (!phy_8960) { - ret = -ENOMEM; - goto fail; - } - - phy = &phy_8960->base; - - phy->funcs = &hdmi_phy_8960_funcs; - - phy_8960->hdmi = hdmi; - -#ifdef CONFIG_COMMON_CLK - phy_8960->pll_hw.init = &pll_init; - phy_8960->pll = devm_clk_register(&hdmi->pdev->dev, &phy_8960->pll_hw); - if (IS_ERR(phy_8960->pll)) { - ret = PTR_ERR(phy_8960->pll); - phy_8960->pll = NULL; - goto fail; - } -#endif - - return phy; +static const char * const hdmi_phy_8960_clk_names[] = { + "slave_iface_clk", +}; -fail: - if (phy) - hdmi_phy_8960_destroy(phy); - return ERR_PTR(ret); -} +const struct hdmi_phy_cfg msm_hdmi_phy_8960_cfg = { + .type = MSM_HDMI_PHY_8960, + .powerup = hdmi_phy_8960_powerup, + .powerdown = hdmi_phy_8960_powerdown, + .reg_names = hdmi_phy_8960_reg_names, + .num_regs = ARRAY_SIZE(hdmi_phy_8960_reg_names), + .clk_names = hdmi_phy_8960_clk_names, + .num_clks = ARRAY_SIZE(hdmi_phy_8960_clk_names), +}; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c new file mode 100644 index 000000000000..aa94a553794f --- /dev/null +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c @@ -0,0 +1,766 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> + +#include "hdmi.h" + +#define HDMI_VCO_MAX_FREQ 12000000000UL +#define HDMI_VCO_MIN_FREQ 8000000000UL + +#define HDMI_PCLK_MAX_FREQ 600000000 +#define HDMI_PCLK_MIN_FREQ 25000000 + +#define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL +#define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL +#define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000UL +#define HDMI_CORECLK_DIV 5 +#define HDMI_DEFAULT_REF_CLOCK 19200000 +#define HDMI_PLL_CMP_CNT 1024 + +#define HDMI_PLL_POLL_MAX_READS 100 +#define HDMI_PLL_POLL_TIMEOUT_US 150 + +#define HDMI_NUM_TX_CHANNEL 4 + +struct hdmi_pll_8996 { + struct platform_device *pdev; + struct clk_hw clk_hw; + + /* pll mmio base */ + void __iomem *mmio_qserdes_com; + /* tx channel base */ + void __iomem *mmio_qserdes_tx[HDMI_NUM_TX_CHANNEL]; +}; + +#define hw_clk_to_pll(x) container_of(x, struct hdmi_pll_8996, clk_hw) + +struct hdmi_8996_phy_pll_reg_cfg { + u32 tx_lx_lane_mode[HDMI_NUM_TX_CHANNEL]; + u32 tx_lx_tx_band[HDMI_NUM_TX_CHANNEL]; + u32 com_svs_mode_clk_sel; + u32 com_hsclk_sel; + u32 com_pll_cctrl_mode0; + u32 com_pll_rctrl_mode0; + u32 com_cp_ctrl_mode0; + u32 com_dec_start_mode0; + u32 com_div_frac_start1_mode0; + u32 com_div_frac_start2_mode0; + u32 com_div_frac_start3_mode0; + u32 com_integloop_gain0_mode0; + u32 com_integloop_gain1_mode0; + u32 com_lock_cmp_en; + u32 com_lock_cmp1_mode0; + u32 com_lock_cmp2_mode0; + u32 com_lock_cmp3_mode0; + u32 com_core_clk_en; + u32 com_coreclk_div; + u32 com_vco_tune_ctrl; + + u32 tx_lx_tx_drv_lvl[HDMI_NUM_TX_CHANNEL]; + u32 tx_lx_tx_emp_post1_lvl[HDMI_NUM_TX_CHANNEL]; + u32 tx_lx_vmode_ctrl1[HDMI_NUM_TX_CHANNEL]; + u32 tx_lx_vmode_ctrl2[HDMI_NUM_TX_CHANNEL]; + u32 tx_lx_res_code_lane_tx[HDMI_NUM_TX_CHANNEL]; + u32 tx_lx_hp_pd_enables[HDMI_NUM_TX_CHANNEL]; + + u32 phy_mode; +}; + +struct hdmi_8996_post_divider { + u64 vco_freq; + int hsclk_divsel; + int vco_ratio; + int tx_band_sel; + int half_rate_mode; +}; + +static inline struct hdmi_phy *pll_get_phy(struct hdmi_pll_8996 *pll) +{ + return platform_get_drvdata(pll->pdev); +} + +static inline void hdmi_pll_write(struct hdmi_pll_8996 *pll, int offset, + u32 data) +{ + msm_writel(data, pll->mmio_qserdes_com + offset); +} + +static inline u32 hdmi_pll_read(struct hdmi_pll_8996 *pll, int offset) +{ + return msm_readl(pll->mmio_qserdes_com + offset); +} + +static inline void hdmi_tx_chan_write(struct hdmi_pll_8996 *pll, int channel, + int offset, int data) +{ + msm_writel(data, pll->mmio_qserdes_tx[channel] + offset); +} + +static inline u32 pll_get_cpctrl(u64 frac_start, unsigned long ref_clk, + bool gen_ssc) +{ + if ((frac_start != 0) || gen_ssc) + return (11000000 / (ref_clk / 20)); + + return 0x23; +} + +static inline u32 pll_get_rctrl(u64 frac_start, bool gen_ssc) +{ + if ((frac_start != 0) || gen_ssc) + return 0x16; + + return 0x10; +} + +static inline u32 pll_get_cctrl(u64 frac_start, bool gen_ssc) +{ + if ((frac_start != 0) || gen_ssc) + return 0x28; + + return 0x1; +} + +static inline u32 pll_get_integloop_gain(u64 frac_start, u64 bclk, u32 ref_clk, + bool gen_ssc) +{ + int digclk_divsel = bclk >= HDMI_DIG_FREQ_BIT_CLK_THRESHOLD ? 1 : 2; + u64 base; + + if ((frac_start != 0) || gen_ssc) + base = (64 * ref_clk) / HDMI_DEFAULT_REF_CLOCK; + else + base = (1022 * ref_clk) / 100; + + base <<= digclk_divsel; + + return (base <= 2046 ? base : 2046); +} + +static inline u32 pll_get_pll_cmp(u64 fdata, unsigned long ref_clk) +{ + u64 dividend = HDMI_PLL_CMP_CNT * fdata; + u32 divisor = ref_clk * 10; + u32 rem; + + rem = do_div(dividend, divisor); + if (rem > (divisor >> 1)) + dividend++; + + return dividend - 1; +} + +static inline u64 pll_cmp_to_fdata(u32 pll_cmp, unsigned long ref_clk) +{ + u64 fdata = ((u64)pll_cmp) * ref_clk * 10; + + do_div(fdata, HDMI_PLL_CMP_CNT); + + return fdata; +} + +static int pll_get_post_div(struct hdmi_8996_post_divider *pd, u64 bclk) +{ + int ratio[] = { 2, 3, 4, 5, 6, 9, 10, 12, 14, 15, 20, 21, 25, 28, 35 }; + int hs_divsel[] = { 0, 4, 8, 12, 1, 5, 2, 9, 3, 13, 10, 7, 14, 11, 15 }; + int tx_band_sel[] = { 0, 1, 2, 3 }; + u64 vco_freq[60]; + u64 vco, vco_optimal; + int half_rate_mode = 0; + int vco_optimal_index, vco_freq_index; + int i, j; + +retry: + vco_optimal = HDMI_VCO_MAX_FREQ; + vco_optimal_index = -1; + vco_freq_index = 0; + for (i = 0; i < 15; i++) { + for (j = 0; j < 4; j++) { + u32 ratio_mult = ratio[i] << tx_band_sel[j]; + + vco = bclk >> half_rate_mode; + vco *= ratio_mult; + vco_freq[vco_freq_index++] = vco; + } + } + + for (i = 0; i < 60; i++) { + u64 vco_tmp = vco_freq[i]; + + if ((vco_tmp >= HDMI_VCO_MIN_FREQ) && + (vco_tmp <= vco_optimal)) { + vco_optimal = vco_tmp; + vco_optimal_index = i; + } + } + + if (vco_optimal_index == -1) { + if (!half_rate_mode) { + half_rate_mode = 1; + goto retry; + } + } else { + pd->vco_freq = vco_optimal; + pd->tx_band_sel = tx_band_sel[vco_optimal_index % 4]; + pd->vco_ratio = ratio[vco_optimal_index / 4]; + pd->hsclk_divsel = hs_divsel[vco_optimal_index / 4]; + + return 0; + } + + return -EINVAL; +} + +static int pll_calculate(unsigned long pix_clk, unsigned long ref_clk, + struct hdmi_8996_phy_pll_reg_cfg *cfg) +{ + struct hdmi_8996_post_divider pd; + u64 bclk; + u64 tmds_clk; + u64 dec_start; + u64 frac_start; + u64 fdata; + u32 pll_divisor; + u32 rem; + u32 cpctrl; + u32 rctrl; + u32 cctrl; + u32 integloop_gain; + u32 pll_cmp; + int i, ret; + + /* bit clk = 10 * pix_clk */ + bclk = ((u64)pix_clk) * 10; + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) + tmds_clk = pix_clk >> 2; + else + tmds_clk = pix_clk; + + ret = pll_get_post_div(&pd, bclk); + if (ret) + return ret; + + dec_start = pd.vco_freq; + pll_divisor = 4 * ref_clk; + do_div(dec_start, pll_divisor); + + frac_start = pd.vco_freq * (1 << 20); + + rem = do_div(frac_start, pll_divisor); + frac_start -= dec_start * (1 << 20); + if (rem > (pll_divisor >> 1)) + frac_start++; + + cpctrl = pll_get_cpctrl(frac_start, ref_clk, false); + rctrl = pll_get_rctrl(frac_start, false); + cctrl = pll_get_cctrl(frac_start, false); + integloop_gain = pll_get_integloop_gain(frac_start, bclk, + ref_clk, false); + + fdata = pd.vco_freq; + do_div(fdata, pd.vco_ratio); + + pll_cmp = pll_get_pll_cmp(fdata, ref_clk); + + DBG("VCO freq: %llu", pd.vco_freq); + DBG("fdata: %llu", fdata); + DBG("pix_clk: %lu", pix_clk); + DBG("tmds clk: %llu", tmds_clk); + DBG("HSCLK_SEL: %d", pd.hsclk_divsel); + DBG("DEC_START: %llu", dec_start); + DBG("DIV_FRAC_START: %llu", frac_start); + DBG("PLL_CPCTRL: %u", cpctrl); + DBG("PLL_RCTRL: %u", rctrl); + DBG("PLL_CCTRL: %u", cctrl); + DBG("INTEGLOOP_GAIN: %u", integloop_gain); + DBG("TX_BAND: %d", pd.tx_band_sel); + DBG("PLL_CMP: %u", pll_cmp); + + /* Convert these values to register specific values */ + if (bclk > HDMI_DIG_FREQ_BIT_CLK_THRESHOLD) + cfg->com_svs_mode_clk_sel = 1; + else + cfg->com_svs_mode_clk_sel = 2; + + cfg->com_hsclk_sel = (0x20 | pd.hsclk_divsel); + cfg->com_pll_cctrl_mode0 = cctrl; + cfg->com_pll_rctrl_mode0 = rctrl; + cfg->com_cp_ctrl_mode0 = cpctrl; + cfg->com_dec_start_mode0 = dec_start; + cfg->com_div_frac_start1_mode0 = (frac_start & 0xff); + cfg->com_div_frac_start2_mode0 = ((frac_start & 0xff00) >> 8); + cfg->com_div_frac_start3_mode0 = ((frac_start & 0xf0000) >> 16); + cfg->com_integloop_gain0_mode0 = (integloop_gain & 0xff); + cfg->com_integloop_gain1_mode0 = ((integloop_gain & 0xf00) >> 8); + cfg->com_lock_cmp1_mode0 = (pll_cmp & 0xff); + cfg->com_lock_cmp2_mode0 = ((pll_cmp & 0xff00) >> 8); + cfg->com_lock_cmp3_mode0 = ((pll_cmp & 0x30000) >> 16); + cfg->com_lock_cmp_en = 0x0; + cfg->com_core_clk_en = 0x2c; + cfg->com_coreclk_div = HDMI_CORECLK_DIV; + cfg->phy_mode = (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) ? 0x10 : 0x0; + cfg->com_vco_tune_ctrl = 0x0; + + cfg->tx_lx_lane_mode[0] = + cfg->tx_lx_lane_mode[2] = 0x43; + + cfg->tx_lx_hp_pd_enables[0] = + cfg->tx_lx_hp_pd_enables[1] = + cfg->tx_lx_hp_pd_enables[2] = 0x0c; + cfg->tx_lx_hp_pd_enables[3] = 0x3; + + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) + cfg->tx_lx_tx_band[i] = pd.tx_band_sel + 4; + + if (bclk > HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD) { + cfg->tx_lx_tx_drv_lvl[0] = + cfg->tx_lx_tx_drv_lvl[1] = + cfg->tx_lx_tx_drv_lvl[2] = 0x25; + cfg->tx_lx_tx_drv_lvl[3] = 0x22; + + cfg->tx_lx_tx_emp_post1_lvl[0] = + cfg->tx_lx_tx_emp_post1_lvl[1] = + cfg->tx_lx_tx_emp_post1_lvl[2] = 0x23; + cfg->tx_lx_tx_emp_post1_lvl[3] = 0x27; + + cfg->tx_lx_vmode_ctrl1[0] = + cfg->tx_lx_vmode_ctrl1[1] = + cfg->tx_lx_vmode_ctrl1[2] = + cfg->tx_lx_vmode_ctrl1[3] = 0x00; + + cfg->tx_lx_vmode_ctrl2[0] = + cfg->tx_lx_vmode_ctrl2[1] = + cfg->tx_lx_vmode_ctrl2[2] = 0x0D; + + cfg->tx_lx_vmode_ctrl2[3] = 0x00; + } else if (bclk > HDMI_MID_FREQ_BIT_CLK_THRESHOLD) { + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { + cfg->tx_lx_tx_drv_lvl[i] = 0x25; + cfg->tx_lx_tx_emp_post1_lvl[i] = 0x23; + cfg->tx_lx_vmode_ctrl1[i] = 0x00; + } + + cfg->tx_lx_vmode_ctrl2[0] = + cfg->tx_lx_vmode_ctrl2[1] = + cfg->tx_lx_vmode_ctrl2[2] = 0x0D; + cfg->tx_lx_vmode_ctrl2[3] = 0x00; + } else { + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { + cfg->tx_lx_tx_drv_lvl[i] = 0x20; + cfg->tx_lx_tx_emp_post1_lvl[i] = 0x20; + cfg->tx_lx_vmode_ctrl1[i] = 0x00; + cfg->tx_lx_vmode_ctrl2[i] = 0x0E; + } + } + + DBG("com_svs_mode_clk_sel = 0x%x", cfg->com_svs_mode_clk_sel); + DBG("com_hsclk_sel = 0x%x", cfg->com_hsclk_sel); + DBG("com_lock_cmp_en = 0x%x", cfg->com_lock_cmp_en); + DBG("com_pll_cctrl_mode0 = 0x%x", cfg->com_pll_cctrl_mode0); + DBG("com_pll_rctrl_mode0 = 0x%x", cfg->com_pll_rctrl_mode0); + DBG("com_cp_ctrl_mode0 = 0x%x", cfg->com_cp_ctrl_mode0); + DBG("com_dec_start_mode0 = 0x%x", cfg->com_dec_start_mode0); + DBG("com_div_frac_start1_mode0 = 0x%x", cfg->com_div_frac_start1_mode0); + DBG("com_div_frac_start2_mode0 = 0x%x", cfg->com_div_frac_start2_mode0); + DBG("com_div_frac_start3_mode0 = 0x%x", cfg->com_div_frac_start3_mode0); + DBG("com_integloop_gain0_mode0 = 0x%x", cfg->com_integloop_gain0_mode0); + DBG("com_integloop_gain1_mode0 = 0x%x", cfg->com_integloop_gain1_mode0); + DBG("com_lock_cmp1_mode0 = 0x%x", cfg->com_lock_cmp1_mode0); + DBG("com_lock_cmp2_mode0 = 0x%x", cfg->com_lock_cmp2_mode0); + DBG("com_lock_cmp3_mode0 = 0x%x", cfg->com_lock_cmp3_mode0); + DBG("com_core_clk_en = 0x%x", cfg->com_core_clk_en); + DBG("com_coreclk_div = 0x%x", cfg->com_coreclk_div); + DBG("phy_mode = 0x%x", cfg->phy_mode); + + DBG("tx_l0_lane_mode = 0x%x", cfg->tx_lx_lane_mode[0]); + DBG("tx_l2_lane_mode = 0x%x", cfg->tx_lx_lane_mode[2]); + + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { + DBG("tx_l%d_tx_band = 0x%x", i, cfg->tx_lx_tx_band[i]); + DBG("tx_l%d_tx_drv_lvl = 0x%x", i, cfg->tx_lx_tx_drv_lvl[i]); + DBG("tx_l%d_tx_emp_post1_lvl = 0x%x", i, + cfg->tx_lx_tx_emp_post1_lvl[i]); + DBG("tx_l%d_vmode_ctrl1 = 0x%x", i, cfg->tx_lx_vmode_ctrl1[i]); + DBG("tx_l%d_vmode_ctrl2 = 0x%x", i, cfg->tx_lx_vmode_ctrl2[i]); + } + + return 0; +} + +static int hdmi_8996_pll_set_clk_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct hdmi_pll_8996 *pll = hw_clk_to_pll(hw); + struct hdmi_phy *phy = pll_get_phy(pll); + struct hdmi_8996_phy_pll_reg_cfg cfg; + int i, ret; + + memset(&cfg, 0x00, sizeof(cfg)); + + ret = pll_calculate(rate, parent_rate, &cfg); + if (ret) { + DRM_ERROR("PLL calculation failed\n"); + return ret; + } + + /* Initially shut down PHY */ + DBG("Disabling PHY"); + hdmi_phy_write(phy, REG_HDMI_8996_PHY_PD_CTL, 0x0); + udelay(500); + + /* Power up sequence */ + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BG_CTRL, 0x04); + + hdmi_phy_write(phy, REG_HDMI_8996_PHY_PD_CTL, 0x1); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_RESETSM_CNTRL, 0x20); + hdmi_phy_write(phy, REG_HDMI_8996_PHY_TX0_TX1_LANE_CTL, 0x0F); + hdmi_phy_write(phy, REG_HDMI_8996_PHY_TX2_TX3_LANE_CTL, 0x0F); + + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { + hdmi_tx_chan_write(pll, i, + REG_HDMI_PHY_QSERDES_TX_LX_CLKBUF_ENABLE, + 0x03); + hdmi_tx_chan_write(pll, i, + REG_HDMI_PHY_QSERDES_TX_LX_TX_BAND, + cfg.tx_lx_tx_band[i]); + hdmi_tx_chan_write(pll, i, + REG_HDMI_PHY_QSERDES_TX_LX_RESET_TSYNC_EN, + 0x03); + } + + hdmi_tx_chan_write(pll, 0, REG_HDMI_PHY_QSERDES_TX_LX_LANE_MODE, + cfg.tx_lx_lane_mode[0]); + hdmi_tx_chan_write(pll, 2, REG_HDMI_PHY_QSERDES_TX_LX_LANE_MODE, + cfg.tx_lx_lane_mode[2]); + + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1E); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x07); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SYSCLK_EN_SEL, 0x37); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SYS_CLK_CTRL, 0x02); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CLK_ENABLE1, 0x0E); + + /* Bypass VCO calibration */ + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SVS_MODE_CLK_SEL, + cfg.com_svs_mode_clk_sel); + + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BG_TRIM, 0x0F); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_PLL_IVCO, 0x0F); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_CTRL, + cfg.com_vco_tune_ctrl); + + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_BG_CTRL, 0x06); + + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CLK_SELECT, 0x30); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_HSCLK_SEL, + cfg.com_hsclk_sel); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP_EN, + cfg.com_lock_cmp_en); + + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_PLL_CCTRL_MODE0, + cfg.com_pll_cctrl_mode0); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_PLL_RCTRL_MODE0, + cfg.com_pll_rctrl_mode0); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CP_CTRL_MODE0, + cfg.com_cp_ctrl_mode0); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DEC_START_MODE0, + cfg.com_dec_start_mode0); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START1_MODE0, + cfg.com_div_frac_start1_mode0); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START2_MODE0, + cfg.com_div_frac_start2_mode0); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_DIV_FRAC_START3_MODE0, + cfg.com_div_frac_start3_mode0); + + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_GAIN0_MODE0, + cfg.com_integloop_gain0_mode0); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_INTEGLOOP_GAIN1_MODE0, + cfg.com_integloop_gain1_mode0); + + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP1_MODE0, + cfg.com_lock_cmp1_mode0); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP2_MODE0, + cfg.com_lock_cmp2_mode0); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP3_MODE0, + cfg.com_lock_cmp3_mode0); + + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_VCO_TUNE_MAP, 0x00); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CORE_CLK_EN, + cfg.com_core_clk_en); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CORECLK_DIV, + cfg.com_coreclk_div); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_CMN_CONFIG, 0x02); + + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_RESCODE_DIV_NUM, 0x15); + + /* TX lanes setup (TX 0/1/2/3) */ + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { + hdmi_tx_chan_write(pll, i, + REG_HDMI_PHY_QSERDES_TX_LX_TX_DRV_LVL, + cfg.tx_lx_tx_drv_lvl[i]); + hdmi_tx_chan_write(pll, i, + REG_HDMI_PHY_QSERDES_TX_LX_TX_EMP_POST1_LVL, + cfg.tx_lx_tx_emp_post1_lvl[i]); + hdmi_tx_chan_write(pll, i, + REG_HDMI_PHY_QSERDES_TX_LX_VMODE_CTRL1, + cfg.tx_lx_vmode_ctrl1[i]); + hdmi_tx_chan_write(pll, i, + REG_HDMI_PHY_QSERDES_TX_LX_VMODE_CTRL2, + cfg.tx_lx_vmode_ctrl2[i]); + hdmi_tx_chan_write(pll, i, + REG_HDMI_PHY_QSERDES_TX_LX_TX_DRV_LVL_OFFSET, + 0x00); + hdmi_tx_chan_write(pll, i, + REG_HDMI_PHY_QSERDES_TX_LX_RES_CODE_LANE_OFFSET, + 0x00); + hdmi_tx_chan_write(pll, i, + REG_HDMI_PHY_QSERDES_TX_LX_TRAN_DRVR_EMP_EN, + 0x03); + hdmi_tx_chan_write(pll, i, + REG_HDMI_PHY_QSERDES_TX_LX_PARRATE_REC_DETECT_IDLE_EN, + 0x40); + hdmi_tx_chan_write(pll, i, + REG_HDMI_PHY_QSERDES_TX_LX_HP_PD_ENABLES, + cfg.tx_lx_hp_pd_enables[i]); + } + + hdmi_phy_write(phy, REG_HDMI_8996_PHY_MODE, cfg.phy_mode); + hdmi_phy_write(phy, REG_HDMI_8996_PHY_PD_CTL, 0x1F); + + /* + * Ensure that vco configuration gets flushed to hardware before + * enabling the PLL + */ + wmb(); + + return 0; +} + +static int hdmi_8996_phy_ready_status(struct hdmi_phy *phy) +{ + u32 nb_tries = HDMI_PLL_POLL_MAX_READS; + unsigned long timeout = HDMI_PLL_POLL_TIMEOUT_US; + u32 status; + int phy_ready = 0; + + DBG("Waiting for PHY ready"); + + while (nb_tries--) { + status = hdmi_phy_read(phy, REG_HDMI_8996_PHY_STATUS); + phy_ready = status & BIT(0); + + if (phy_ready) + break; + + udelay(timeout); + } + + DBG("PHY is %sready", phy_ready ? "" : "*not* "); + + return phy_ready; +} + +static int hdmi_8996_pll_lock_status(struct hdmi_pll_8996 *pll) +{ + u32 status; + int nb_tries = HDMI_PLL_POLL_MAX_READS; + unsigned long timeout = HDMI_PLL_POLL_TIMEOUT_US; + int pll_locked = 0; + + DBG("Waiting for PLL lock"); + + while (nb_tries--) { + status = hdmi_pll_read(pll, + REG_HDMI_PHY_QSERDES_COM_C_READY_STATUS); + pll_locked = status & BIT(0); + + if (pll_locked) + break; + + udelay(timeout); + } + + DBG("HDMI PLL is %slocked", pll_locked ? "" : "*not* "); + + return pll_locked; +} + +static int hdmi_8996_pll_prepare(struct clk_hw *hw) +{ + struct hdmi_pll_8996 *pll = hw_clk_to_pll(hw); + struct hdmi_phy *phy = pll_get_phy(pll); + int i, ret = 0; + + hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x1); + udelay(100); + + hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x19); + udelay(100); + + ret = hdmi_8996_pll_lock_status(pll); + if (!ret) + return ret; + + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) + hdmi_tx_chan_write(pll, i, + REG_HDMI_PHY_QSERDES_TX_LX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, + 0x6F); + + /* Disable SSC */ + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_PER1, 0x0); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_PER2, 0x0); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_STEP_SIZE1, 0x0); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_STEP_SIZE2, 0x0); + hdmi_pll_write(pll, REG_HDMI_PHY_QSERDES_COM_SSC_EN_CENTER, 0x2); + + ret = hdmi_8996_phy_ready_status(phy); + if (!ret) + return ret; + + /* Restart the retiming buffer */ + hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x18); + udelay(1); + hdmi_phy_write(phy, REG_HDMI_8996_PHY_CFG, 0x19); + + return 0; +} + +static long hdmi_8996_pll_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *parent_rate) +{ + if (rate < HDMI_PCLK_MIN_FREQ) + return HDMI_PCLK_MIN_FREQ; + else if (rate > HDMI_PCLK_MAX_FREQ) + return HDMI_PCLK_MAX_FREQ; + else + return rate; +} + +static unsigned long hdmi_8996_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct hdmi_pll_8996 *pll = hw_clk_to_pll(hw); + u64 fdata; + u32 cmp1, cmp2, cmp3, pll_cmp; + + cmp1 = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP1_MODE0); + cmp2 = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP2_MODE0); + cmp3 = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_LOCK_CMP3_MODE0); + + pll_cmp = cmp1 | (cmp2 << 8) | (cmp3 << 16); + + fdata = pll_cmp_to_fdata(pll_cmp + 1, parent_rate); + + do_div(fdata, 10); + + return fdata; +} + +static void hdmi_8996_pll_unprepare(struct clk_hw *hw) +{ +} + +static int hdmi_8996_pll_is_enabled(struct clk_hw *hw) +{ + struct hdmi_pll_8996 *pll = hw_clk_to_pll(hw); + u32 status; + int pll_locked; + + status = hdmi_pll_read(pll, REG_HDMI_PHY_QSERDES_COM_C_READY_STATUS); + pll_locked = status & BIT(0); + + return pll_locked; +} + +static struct clk_ops hdmi_8996_pll_ops = { + .set_rate = hdmi_8996_pll_set_clk_rate, + .round_rate = hdmi_8996_pll_round_rate, + .recalc_rate = hdmi_8996_pll_recalc_rate, + .prepare = hdmi_8996_pll_prepare, + .unprepare = hdmi_8996_pll_unprepare, + .is_enabled = hdmi_8996_pll_is_enabled, +}; + +static const char * const hdmi_pll_parents[] = { + "xo", +}; + +static struct clk_init_data pll_init = { + .name = "hdmipll", + .ops = &hdmi_8996_pll_ops, + .parent_names = hdmi_pll_parents, + .num_parents = ARRAY_SIZE(hdmi_pll_parents), +}; + +int msm_hdmi_pll_8996_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct hdmi_pll_8996 *pll; + struct clk *clk; + int i; + + pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); + if (!pll) + return -ENOMEM; + + pll->pdev = pdev; + + pll->mmio_qserdes_com = msm_ioremap(pdev, "hdmi_pll", "HDMI_PLL"); + if (IS_ERR(pll->mmio_qserdes_com)) { + dev_err(dev, "failed to map pll base\n"); + return -ENOMEM; + } + + for (i = 0; i < HDMI_NUM_TX_CHANNEL; i++) { + char name[32], label[32]; + + snprintf(name, sizeof(name), "hdmi_tx_l%d", i); + snprintf(label, sizeof(label), "HDMI_TX_L%d", i); + + pll->mmio_qserdes_tx[i] = msm_ioremap(pdev, name, label); + if (IS_ERR(pll->mmio_qserdes_tx[i])) { + dev_err(dev, "failed to map pll base\n"); + return -ENOMEM; + } + } + pll->clk_hw.init = &pll_init; + + clk = devm_clk_register(dev, &pll->clk_hw); + if (IS_ERR(clk)) { + dev_err(dev, "failed to register pll clock\n"); + return -EINVAL; + } + + return 0; +} + +static const char * const hdmi_phy_8996_reg_names[] = { + "vddio", + "vcca", +}; + +static const char * const hdmi_phy_8996_clk_names[] = { + "mmagic_iface_clk", + "iface_clk", + "ref_clk", +}; + +const struct hdmi_phy_cfg msm_hdmi_phy_8996_cfg = { + .type = MSM_HDMI_PHY_8996, + .reg_names = hdmi_phy_8996_reg_names, + .num_regs = ARRAY_SIZE(hdmi_phy_8996_reg_names), + .clk_names = hdmi_phy_8996_clk_names, + .num_clks = ARRAY_SIZE(hdmi_phy_8996_clk_names), +}; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c index cb01421ae1e4..a68eea4153fc 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x60.c @@ -17,166 +17,122 @@ #include "hdmi.h" -struct hdmi_phy_8x60 { - struct hdmi_phy base; - struct hdmi *hdmi; -}; -#define to_hdmi_phy_8x60(x) container_of(x, struct hdmi_phy_8x60, base) - -static void hdmi_phy_8x60_destroy(struct hdmi_phy *phy) -{ - struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy); - kfree(phy_8x60); -} - static void hdmi_phy_8x60_powerup(struct hdmi_phy *phy, unsigned long int pixclock) { - struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy); - struct hdmi *hdmi = phy_8x60->hdmi; - /* De-serializer delay D/C for non-lbk mode: */ - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG0, - HDMI_8x60_PHY_REG0_DESER_DEL_CTRL(3)); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG0, + HDMI_8x60_PHY_REG0_DESER_DEL_CTRL(3)); if (pixclock == 27000000) { /* video_format == HDMI_VFRMT_720x480p60_16_9 */ - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG1, - HDMI_8x60_PHY_REG1_DTEST_MUX_SEL(5) | - HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(3)); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG1, + HDMI_8x60_PHY_REG1_DTEST_MUX_SEL(5) | + HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(3)); } else { - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG1, - HDMI_8x60_PHY_REG1_DTEST_MUX_SEL(5) | - HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(4)); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG1, + HDMI_8x60_PHY_REG1_DTEST_MUX_SEL(5) | + HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL(4)); } /* No matter what, start from the power down mode: */ - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2, - HDMI_8x60_PHY_REG2_PD_PWRGEN | - HDMI_8x60_PHY_REG2_PD_PLL | - HDMI_8x60_PHY_REG2_PD_DRIVE_4 | - HDMI_8x60_PHY_REG2_PD_DRIVE_3 | - HDMI_8x60_PHY_REG2_PD_DRIVE_2 | - HDMI_8x60_PHY_REG2_PD_DRIVE_1 | - HDMI_8x60_PHY_REG2_PD_DESER); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG2, + HDMI_8x60_PHY_REG2_PD_PWRGEN | + HDMI_8x60_PHY_REG2_PD_PLL | + HDMI_8x60_PHY_REG2_PD_DRIVE_4 | + HDMI_8x60_PHY_REG2_PD_DRIVE_3 | + HDMI_8x60_PHY_REG2_PD_DRIVE_2 | + HDMI_8x60_PHY_REG2_PD_DRIVE_1 | + HDMI_8x60_PHY_REG2_PD_DESER); /* Turn PowerGen on: */ - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2, - HDMI_8x60_PHY_REG2_PD_PLL | - HDMI_8x60_PHY_REG2_PD_DRIVE_4 | - HDMI_8x60_PHY_REG2_PD_DRIVE_3 | - HDMI_8x60_PHY_REG2_PD_DRIVE_2 | - HDMI_8x60_PHY_REG2_PD_DRIVE_1 | - HDMI_8x60_PHY_REG2_PD_DESER); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG2, + HDMI_8x60_PHY_REG2_PD_PLL | + HDMI_8x60_PHY_REG2_PD_DRIVE_4 | + HDMI_8x60_PHY_REG2_PD_DRIVE_3 | + HDMI_8x60_PHY_REG2_PD_DRIVE_2 | + HDMI_8x60_PHY_REG2_PD_DRIVE_1 | + HDMI_8x60_PHY_REG2_PD_DESER); /* Turn PLL power on: */ - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2, - HDMI_8x60_PHY_REG2_PD_DRIVE_4 | - HDMI_8x60_PHY_REG2_PD_DRIVE_3 | - HDMI_8x60_PHY_REG2_PD_DRIVE_2 | - HDMI_8x60_PHY_REG2_PD_DRIVE_1 | - HDMI_8x60_PHY_REG2_PD_DESER); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG2, + HDMI_8x60_PHY_REG2_PD_DRIVE_4 | + HDMI_8x60_PHY_REG2_PD_DRIVE_3 | + HDMI_8x60_PHY_REG2_PD_DRIVE_2 | + HDMI_8x60_PHY_REG2_PD_DRIVE_1 | + HDMI_8x60_PHY_REG2_PD_DESER); /* Write to HIGH after PLL power down de-assert: */ - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG3, - HDMI_8x60_PHY_REG3_PLL_ENABLE); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG3, + HDMI_8x60_PHY_REG3_PLL_ENABLE); /* ASIC power on; PHY REG9 = 0 */ - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG9, 0); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG9, 0); /* Enable PLL lock detect, PLL lock det will go high after lock * Enable the re-time logic */ - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG12, - HDMI_8x60_PHY_REG12_RETIMING_EN | - HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG12, + HDMI_8x60_PHY_REG12_RETIMING_EN | + HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN); /* Drivers are on: */ - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2, - HDMI_8x60_PHY_REG2_PD_DESER); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG2, + HDMI_8x60_PHY_REG2_PD_DESER); /* If the RX detector is needed: */ - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2, - HDMI_8x60_PHY_REG2_RCV_SENSE_EN | - HDMI_8x60_PHY_REG2_PD_DESER); - - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG4, 0); - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG5, 0); - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG6, 0); - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG7, 0); - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG8, 0); - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG9, 0); - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG10, 0); - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG11, 0); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG2, + HDMI_8x60_PHY_REG2_RCV_SENSE_EN | + HDMI_8x60_PHY_REG2_PD_DESER); + + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG4, 0); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG5, 0); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG6, 0); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG7, 0); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG8, 0); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG9, 0); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG10, 0); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG11, 0); /* If we want to use lock enable based on counting: */ - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG12, - HDMI_8x60_PHY_REG12_RETIMING_EN | - HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN | - HDMI_8x60_PHY_REG12_FORCE_LOCK); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG12, + HDMI_8x60_PHY_REG12_RETIMING_EN | + HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN | + HDMI_8x60_PHY_REG12_FORCE_LOCK); } static void hdmi_phy_8x60_powerdown(struct hdmi_phy *phy) { - struct hdmi_phy_8x60 *phy_8x60 = to_hdmi_phy_8x60(phy); - struct hdmi *hdmi = phy_8x60->hdmi; - /* Assert RESET PHY from controller */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, - HDMI_PHY_CTRL_SW_RESET); + hdmi_phy_write(phy, REG_HDMI_PHY_CTRL, + HDMI_PHY_CTRL_SW_RESET); udelay(10); /* De-assert RESET PHY from controller */ - hdmi_write(hdmi, REG_HDMI_PHY_CTRL, 0); + hdmi_phy_write(phy, REG_HDMI_PHY_CTRL, 0); /* Turn off Driver */ - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2, - HDMI_8x60_PHY_REG2_PD_DRIVE_4 | - HDMI_8x60_PHY_REG2_PD_DRIVE_3 | - HDMI_8x60_PHY_REG2_PD_DRIVE_2 | - HDMI_8x60_PHY_REG2_PD_DRIVE_1 | - HDMI_8x60_PHY_REG2_PD_DESER); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG2, + HDMI_8x60_PHY_REG2_PD_DRIVE_4 | + HDMI_8x60_PHY_REG2_PD_DRIVE_3 | + HDMI_8x60_PHY_REG2_PD_DRIVE_2 | + HDMI_8x60_PHY_REG2_PD_DRIVE_1 | + HDMI_8x60_PHY_REG2_PD_DESER); udelay(10); /* Disable PLL */ - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG3, 0); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG3, 0); /* Power down PHY, but keep RX-sense: */ - hdmi_write(hdmi, REG_HDMI_8x60_PHY_REG2, - HDMI_8x60_PHY_REG2_RCV_SENSE_EN | - HDMI_8x60_PHY_REG2_PD_PWRGEN | - HDMI_8x60_PHY_REG2_PD_PLL | - HDMI_8x60_PHY_REG2_PD_DRIVE_4 | - HDMI_8x60_PHY_REG2_PD_DRIVE_3 | - HDMI_8x60_PHY_REG2_PD_DRIVE_2 | - HDMI_8x60_PHY_REG2_PD_DRIVE_1 | - HDMI_8x60_PHY_REG2_PD_DESER); + hdmi_phy_write(phy, REG_HDMI_8x60_PHY_REG2, + HDMI_8x60_PHY_REG2_RCV_SENSE_EN | + HDMI_8x60_PHY_REG2_PD_PWRGEN | + HDMI_8x60_PHY_REG2_PD_PLL | + HDMI_8x60_PHY_REG2_PD_DRIVE_4 | + HDMI_8x60_PHY_REG2_PD_DRIVE_3 | + HDMI_8x60_PHY_REG2_PD_DRIVE_2 | + HDMI_8x60_PHY_REG2_PD_DRIVE_1 | + HDMI_8x60_PHY_REG2_PD_DESER); } -static const struct hdmi_phy_funcs hdmi_phy_8x60_funcs = { - .destroy = hdmi_phy_8x60_destroy, - .powerup = hdmi_phy_8x60_powerup, - .powerdown = hdmi_phy_8x60_powerdown, +const struct hdmi_phy_cfg msm_hdmi_phy_8x60_cfg = { + .type = MSM_HDMI_PHY_8x60, + .powerup = hdmi_phy_8x60_powerup, + .powerdown = hdmi_phy_8x60_powerdown, }; - -struct hdmi_phy *hdmi_phy_8x60_init(struct hdmi *hdmi) -{ - struct hdmi_phy_8x60 *phy_8x60; - struct hdmi_phy *phy = NULL; - int ret; - - phy_8x60 = kzalloc(sizeof(*phy_8x60), GFP_KERNEL); - if (!phy_8x60) { - ret = -ENOMEM; - goto fail; - } - - phy = &phy_8x60->base; - - phy->funcs = &hdmi_phy_8x60_funcs; - - phy_8x60->hdmi = hdmi; - - return phy; - -fail: - if (phy) - hdmi_phy_8x60_destroy(phy); - return ERR_PTR(ret); -} diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c index 56ab8917ee9a..c4a61e537851 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8x74.c @@ -17,84 +17,40 @@ #include "hdmi.h" -struct hdmi_phy_8x74 { - struct hdmi_phy base; - void __iomem *mmio; -}; -#define to_hdmi_phy_8x74(x) container_of(x, struct hdmi_phy_8x74, base) - - -static void phy_write(struct hdmi_phy_8x74 *phy, u32 reg, u32 data) -{ - msm_writel(data, phy->mmio + reg); -} - -//static u32 phy_read(struct hdmi_phy_8x74 *phy, u32 reg) -//{ -// return msm_readl(phy->mmio + reg); -//} - -static void hdmi_phy_8x74_destroy(struct hdmi_phy *phy) -{ - struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy); - kfree(phy_8x74); -} - static void hdmi_phy_8x74_powerup(struct hdmi_phy *phy, unsigned long int pixclock) { - struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy); - - phy_write(phy_8x74, REG_HDMI_8x74_ANA_CFG0, 0x1b); - phy_write(phy_8x74, REG_HDMI_8x74_ANA_CFG1, 0xf2); - phy_write(phy_8x74, REG_HDMI_8x74_BIST_CFG0, 0x0); - phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN0, 0x0); - phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN1, 0x0); - phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN2, 0x0); - phy_write(phy_8x74, REG_HDMI_8x74_BIST_PATN3, 0x0); - phy_write(phy_8x74, REG_HDMI_8x74_PD_CTRL1, 0x20); + hdmi_phy_write(phy, REG_HDMI_8x74_ANA_CFG0, 0x1b); + hdmi_phy_write(phy, REG_HDMI_8x74_ANA_CFG1, 0xf2); + hdmi_phy_write(phy, REG_HDMI_8x74_BIST_CFG0, 0x0); + hdmi_phy_write(phy, REG_HDMI_8x74_BIST_PATN0, 0x0); + hdmi_phy_write(phy, REG_HDMI_8x74_BIST_PATN1, 0x0); + hdmi_phy_write(phy, REG_HDMI_8x74_BIST_PATN2, 0x0); + hdmi_phy_write(phy, REG_HDMI_8x74_BIST_PATN3, 0x0); + hdmi_phy_write(phy, REG_HDMI_8x74_PD_CTRL1, 0x20); } static void hdmi_phy_8x74_powerdown(struct hdmi_phy *phy) { - struct hdmi_phy_8x74 *phy_8x74 = to_hdmi_phy_8x74(phy); - phy_write(phy_8x74, REG_HDMI_8x74_PD_CTRL0, 0x7f); + hdmi_phy_write(phy, REG_HDMI_8x74_PD_CTRL0, 0x7f); } -static const struct hdmi_phy_funcs hdmi_phy_8x74_funcs = { - .destroy = hdmi_phy_8x74_destroy, - .powerup = hdmi_phy_8x74_powerup, - .powerdown = hdmi_phy_8x74_powerdown, +static const char * const hdmi_phy_8x74_reg_names[] = { + "core-vdda", + "vddio", }; -struct hdmi_phy *hdmi_phy_8x74_init(struct hdmi *hdmi) -{ - struct hdmi_phy_8x74 *phy_8x74; - struct hdmi_phy *phy = NULL; - int ret; - - phy_8x74 = kzalloc(sizeof(*phy_8x74), GFP_KERNEL); - if (!phy_8x74) { - ret = -ENOMEM; - goto fail; - } - - phy = &phy_8x74->base; - - phy->funcs = &hdmi_phy_8x74_funcs; - - /* for 8x74, the phy mmio is mapped separately: */ - phy_8x74->mmio = msm_ioremap(hdmi->pdev, - "phy_physical", "HDMI_8x74"); - if (IS_ERR(phy_8x74->mmio)) { - ret = PTR_ERR(phy_8x74->mmio); - goto fail; - } - - return phy; +static const char * const hdmi_phy_8x74_clk_names[] = { + "iface_clk", + "alt_iface_clk" +}; -fail: - if (phy) - hdmi_phy_8x74_destroy(phy); - return ERR_PTR(ret); -} +const struct hdmi_phy_cfg msm_hdmi_phy_8x74_cfg = { + .type = MSM_HDMI_PHY_8x74, + .powerup = hdmi_phy_8x74_powerup, + .powerdown = hdmi_phy_8x74_powerdown, + .reg_names = hdmi_phy_8x74_reg_names, + .num_regs = ARRAY_SIZE(hdmi_phy_8x74_reg_names), + .clk_names = hdmi_phy_8x74_clk_names, + .num_clks = ARRAY_SIZE(hdmi_phy_8x74_clk_names), +}; diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c b/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c new file mode 100644 index 000000000000..92da69aa6187 --- /dev/null +++ b/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (C) 2013 Red Hat + * Author: Rob Clark <robdclark@gmail.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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk-provider.h> +#include "hdmi.h" + +struct hdmi_pll_8960 { + struct platform_device *pdev; + struct clk_hw clk_hw; + void __iomem *mmio; + + unsigned long pixclk; +}; + +#define hw_clk_to_pll(x) container_of(x, struct hdmi_pll_8960, clk_hw) + +/* + * HDMI PLL: + * + * To get the parent clock setup properly, we need to plug in hdmi pll + * configuration into common-clock-framework. + */ + +struct pll_rate { + unsigned long rate; + int num_reg; + struct { + u32 val; + u32 reg; + } conf[32]; +}; + +/* NOTE: keep sorted highest freq to lowest: */ +static const struct pll_rate freqtbl[] = { + { 154000000, 14, { + { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, + { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, + { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, + { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, + { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, + { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, + { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, + { 0x0d, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, + { 0x4d, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, + { 0x5e, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, + { 0x42, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, + } + }, + /* 1080p60/1080p50 case */ + { 148500000, 27, { + { 0x02, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, + { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, + { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, + { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, + { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG }, + { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG }, + { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, + { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, + { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, + { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, + { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, + { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3 }, + { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 }, + { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 }, + { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 }, + { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, + { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, + { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, + { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, + { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, + } + }, + { 108000000, 13, { + { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, + { 0x21, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, + { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, + { 0x1c, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, + { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, + { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, + { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, + { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, + { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, + } + }, + /* 720p60/720p50/1080i60/1080i50/1080p24/1080p30/1080p25 */ + { 74250000, 8, { + { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, + { 0x12, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, + { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, + { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, + { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, + { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, + { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, + { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, + } + }, + { 74176000, 14, { + { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, + { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, + { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, + { 0xe5, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, + { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, + { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, + { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, + { 0x0c, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, + { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, + { 0x7d, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, + { 0xbc, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, + } + }, + { 65000000, 14, { + { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, + { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, + { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, + { 0x8a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, + { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, + { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, + { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, + { 0x0b, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, + { 0x4b, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, + { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, + { 0x09, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, + } + }, + /* 480p60/480i60 */ + { 27030000, 18, { + { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, + { 0x38, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, + { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, + { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, + { 0xff, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, + { 0x4e, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, + { 0xd7, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, + { 0x03, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, + { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, + { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, + { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, + { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, + { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, + } + }, + /* 576p50/576i50 */ + { 27000000, 27, { + { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, + { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, + { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, + { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, + { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG }, + { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG }, + { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, + { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, + { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, + { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, + { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, + { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3 }, + { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 }, + { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 }, + { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 }, + { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, + { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, + { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, + { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, + { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, + } + }, + /* 640x480p60 */ + { 25200000, 27, { + { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, + { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, + { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, + { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, + { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG }, + { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG }, + { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, + { 0x77, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, + { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, + { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, + { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 }, + { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 }, + { 0x20, REG_HDMI_8960_PHY_PLL_SSC_CFG3 }, + { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 }, + { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 }, + { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 }, + { 0xf4, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, + { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, + { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, + { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, + { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, + { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, + } + }, +}; + +static inline void pll_write(struct hdmi_pll_8960 *pll, u32 reg, u32 data) +{ + msm_writel(data, pll->mmio + reg); +} + +static inline u32 pll_read(struct hdmi_pll_8960 *pll, u32 reg) +{ + return msm_readl(pll->mmio + reg); +} + +static inline struct hdmi_phy *pll_get_phy(struct hdmi_pll_8960 *pll) +{ + return platform_get_drvdata(pll->pdev); +} + +static int hdmi_pll_enable(struct clk_hw *hw) +{ + struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw); + struct hdmi_phy *phy = pll_get_phy(pll); + int timeout_count, pll_lock_retry = 10; + unsigned int val; + + DBG(""); + + /* Assert PLL S/W reset */ + pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d); + pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0, 0x10); + pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1, 0x1a); + + /* Wait for a short time before de-asserting + * to allow the hardware to complete its job. + * This much of delay should be fine for hardware + * to assert and de-assert. + */ + udelay(10); + + /* De-assert PLL S/W reset */ + pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d); + + val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12); + val |= HDMI_8960_PHY_REG12_SW_RESET; + /* Assert PHY S/W reset */ + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val); + val &= ~HDMI_8960_PHY_REG12_SW_RESET; + /* + * Wait for a short time before de-asserting to allow the hardware to + * complete its job. This much of delay should be fine for hardware to + * assert and de-assert. + */ + udelay(10); + /* De-assert PHY S/W reset */ + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2, 0x3f); + + val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12); + val |= HDMI_8960_PHY_REG12_PWRDN_B; + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val); + /* Wait 10 us for enabling global power for PHY */ + mb(); + udelay(10); + + val = pll_read(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B); + val |= HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B; + val &= ~HDMI_8960_PHY_PLL_PWRDN_B_PD_PLL; + pll_write(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B, val); + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2, 0x80); + + timeout_count = 1000; + while (--pll_lock_retry > 0) { + /* are we there yet? */ + val = pll_read(pll, REG_HDMI_8960_PHY_PLL_STATUS0); + if (val & HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK) + break; + + udelay(1); + + if (--timeout_count > 0) + continue; + + /* + * PLL has still not locked. + * Do a software reset and try again + * Assert PLL S/W reset first + */ + pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d); + udelay(10); + pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d); + + /* + * Wait for a short duration for the PLL calibration + * before checking if the PLL gets locked + */ + udelay(350); + + timeout_count = 1000; + } + + return 0; +} + +static void hdmi_pll_disable(struct clk_hw *hw) +{ + struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw); + struct hdmi_phy *phy = pll_get_phy(pll); + unsigned int val; + + DBG(""); + + val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12); + val &= ~HDMI_8960_PHY_REG12_PWRDN_B; + hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, val); + + val = pll_read(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B); + val |= HDMI_8960_PHY_REG12_SW_RESET; + val &= ~HDMI_8960_PHY_REG12_PWRDN_B; + pll_write(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B, val); + /* Make sure HDMI PHY/PLL are powered down */ + mb(); +} + +static const struct pll_rate *find_rate(unsigned long rate) +{ + int i; + + for (i = 1; i < ARRAY_SIZE(freqtbl); i++) + if (rate > freqtbl[i].rate) + return &freqtbl[i - 1]; + + return &freqtbl[i - 1]; +} + +static unsigned long hdmi_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw); + + return pll->pixclk; +} + +static long hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + const struct pll_rate *pll_rate = find_rate(rate); + + return pll_rate->rate; +} + +static int hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw); + const struct pll_rate *pll_rate = find_rate(rate); + int i; + + DBG("rate=%lu", rate); + + for (i = 0; i < pll_rate->num_reg; i++) + pll_write(pll, pll_rate->conf[i].reg, pll_rate->conf[i].val); + + pll->pixclk = rate; + + return 0; +} + +static const struct clk_ops hdmi_pll_ops = { + .enable = hdmi_pll_enable, + .disable = hdmi_pll_disable, + .recalc_rate = hdmi_pll_recalc_rate, + .round_rate = hdmi_pll_round_rate, + .set_rate = hdmi_pll_set_rate, +}; + +static const char * const hdmi_pll_parents[] = { + "pxo", +}; + +static struct clk_init_data pll_init = { + .name = "hdmi_pll", + .ops = &hdmi_pll_ops, + .parent_names = hdmi_pll_parents, + .num_parents = ARRAY_SIZE(hdmi_pll_parents), +}; + +int msm_hdmi_pll_8960_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct hdmi_pll_8960 *pll; + struct clk *clk; + int i; + + /* sanity check: */ + for (i = 0; i < (ARRAY_SIZE(freqtbl) - 1); i++) + if (WARN_ON(freqtbl[i].rate < freqtbl[i + 1].rate)) + return -EINVAL; + + pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL); + if (!pll) + return -ENOMEM; + + pll->mmio = msm_ioremap(pdev, "hdmi_pll", "HDMI_PLL"); + if (IS_ERR(pll->mmio)) { + dev_err(dev, "failed to map pll base\n"); + return -ENOMEM; + } + + pll->pdev = pdev; + pll->clk_hw.init = &pll_init; + + clk = devm_clk_register(dev, &pll->clk_hw); + if (IS_ERR(clk)) { + dev_err(dev, "failed to register pll clock\n"); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/gpu/drm/msm/hdmi/qfprom.xml.h b/drivers/gpu/drm/msm/hdmi/qfprom.xml.h index dbd9cc4daf2e..6eab7d0cf6b5 100644 --- a/drivers/gpu/drm/msm/hdmi/qfprom.xml.h +++ b/drivers/gpu/drm/msm/hdmi/qfprom.xml.h @@ -9,7 +9,7 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 37194 bytes, from 2015-09-18 12:07:28) @@ -17,11 +17,12 @@ The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 41472 bytes, from 2016-01-22 18:18:18) - /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) +- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h index d5d94575fa1b..6688e79cc88e 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4.xml.h @@ -9,7 +9,7 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 37194 bytes, from 2015-09-18 12:07:28) @@ -17,11 +17,12 @@ The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 41472 bytes, from 2016-01-22 18:18:18) - /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) +- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c index 909d74250de7..c77e3d4e2c5c 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c @@ -361,13 +361,6 @@ static void mdp4_crtc_atomic_flush(struct drm_crtc *crtc, request_pending(crtc, PENDING_FLIP); } -static int mdp4_crtc_set_property(struct drm_crtc *crtc, - struct drm_property *property, uint64_t val) -{ - // XXX - return -EINVAL; -} - #define CURSOR_WIDTH 64 #define CURSOR_HEIGHT 64 @@ -499,7 +492,7 @@ static const struct drm_crtc_funcs mdp4_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .destroy = mdp4_crtc_destroy, .page_flip = drm_atomic_helper_page_flip, - .set_property = mdp4_crtc_set_property, + .set_property = drm_atomic_helper_crtc_set_property, .cursor_set = mdp4_crtc_cursor_set, .cursor_move = mdp4_crtc_cursor_move, .reset = drm_atomic_helper_crtc_reset, diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index 1c8e330f8d98..76e1dfb5d25e 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -179,9 +179,20 @@ static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate, } } +static const char * const iommu_ports[] = { + "mdp_port0_cb0", "mdp_port1_cb0", +}; + static void mdp4_destroy(struct msm_kms *kms) { struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms)); + struct msm_mmu *mmu = mdp4_kms->mmu; + + if (mmu) { + mmu->funcs->detach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports)); + mmu->funcs->destroy(mmu); + } + if (mdp4_kms->blank_cursor_iova) msm_gem_put_iova(mdp4_kms->blank_cursor_bo, mdp4_kms->id); if (mdp4_kms->blank_cursor_bo) @@ -315,7 +326,7 @@ static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms, if (priv->hdmi) { /* Construct bridge/connector for HDMI: */ - ret = hdmi_modeset_init(priv->hdmi, dev, encoder); + ret = msm_hdmi_modeset_init(priv->hdmi, dev, encoder); if (ret) { dev_err(dev->dev, "failed to initialize HDMI: %d\n", ret); return ret; @@ -446,10 +457,6 @@ fail: return ret; } -static const char *iommu_ports[] = { - "mdp_port0_cb0", "mdp_port1_cb0", -}; - struct msm_kms *mdp4_kms_init(struct drm_device *dev) { struct platform_device *pdev = dev->platformdev; @@ -554,6 +561,8 @@ struct msm_kms *mdp4_kms_init(struct drm_device *dev) ARRAY_SIZE(iommu_ports)); if (ret) goto fail; + + mdp4_kms->mmu = mmu; } else { dev_info(dev->dev, "no iommu, fallback to phys " "contig buffers for scanout\n"); diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h index 9ec53b464662..b2828717be2a 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.h @@ -45,6 +45,7 @@ struct mdp4_kms { struct clk *pclk; struct clk *lut_clk; struct clk *axi_clk; + struct msm_mmu *mmu; struct mdp_irq error_handler; diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h index c37da9c61e29..b275ce11b24b 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5.xml.h @@ -9,7 +9,7 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 37194 bytes, from 2015-09-18 12:07:28) @@ -17,11 +17,12 @@ The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 41472 bytes, from 2016-01-22 18:18:18) - /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) +- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 46682aa8870c..a064d9712234 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -468,13 +468,6 @@ static void mdp5_crtc_atomic_flush(struct drm_crtc *crtc, request_pending(crtc, PENDING_FLIP); } -static int mdp5_crtc_set_property(struct drm_crtc *crtc, - struct drm_property *property, uint64_t val) -{ - // XXX - return -EINVAL; -} - static void get_roi(struct drm_crtc *crtc, uint32_t *roi_w, uint32_t *roi_h) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); @@ -625,7 +618,7 @@ static const struct drm_crtc_funcs mdp5_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .destroy = mdp5_crtc_destroy, .page_flip = drm_atomic_helper_page_flip, - .set_property = mdp5_crtc_set_property, + .set_property = drm_atomic_helper_crtc_set_property, .reset = drm_atomic_helper_crtc_reset, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index 5e4d16b399c7..484b4d15e71d 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -284,7 +284,7 @@ static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num) break; } - ret = hdmi_modeset_init(priv->hdmi, dev, encoder); + ret = msm_hdmi_modeset_init(priv->hdmi, dev, encoder); break; case INTF_DSI: { diff --git a/drivers/gpu/drm/msm/mdp/mdp_common.xml.h b/drivers/gpu/drm/msm/mdp/mdp_common.xml.h index 0aec1ac1f6d0..452e3518f98b 100644 --- a/drivers/gpu/drm/msm/mdp/mdp_common.xml.h +++ b/drivers/gpu/drm/msm/mdp/mdp_common.xml.h @@ -9,7 +9,7 @@ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1572 bytes, from 2016-02-10 17:07:21) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) - /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 37194 bytes, from 2015-09-18 12:07:28) @@ -17,11 +17,12 @@ The rules-ng-ng source files this header was generated from are: - /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) - /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) - /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 41472 bytes, from 2016-01-22 18:18:18) - /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) +- Ilia Mirkin <imirkin@alum.mit.edu> (imirkin) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 9a30807b900b..d52910e2c26c 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -61,7 +61,7 @@ module_param(fbdev, bool, 0600); #endif static char *vram = "16m"; -MODULE_PARM_DESC(vram, "Configure VRAM size (for devices without IOMMU/GPUMMU"); +MODULE_PARM_DESC(vram, "Configure VRAM size (for devices without IOMMU/GPUMMU)"); module_param(vram, charp, 0); /* @@ -196,6 +196,11 @@ static int msm_unload(struct drm_device *dev) } drm_kms_helper_poll_fini(dev); + +#ifdef CONFIG_DRM_FBDEV_EMULATION + if (fbdev && priv->fbdev) + msm_fbdev_free(dev); +#endif drm_mode_config_cleanup(dev); drm_vblank_cleanup(dev); @@ -1116,7 +1121,7 @@ static int __init msm_drm_register(void) DBG("init"); msm_dsi_register(); msm_edp_register(); - hdmi_register(); + msm_hdmi_register(); adreno_register(); return platform_driver_register(&msm_platform_driver); } @@ -1125,7 +1130,7 @@ static void __exit msm_drm_unregister(void) { DBG("fini"); platform_driver_unregister(&msm_platform_driver); - hdmi_unregister(); + msm_hdmi_unregister(); adreno_unregister(); msm_edp_unregister(); msm_dsi_unregister(); diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index c1e7bba2fdb7..870dbe58c259 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -240,12 +240,13 @@ struct drm_framebuffer *msm_framebuffer_create(struct drm_device *dev, struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd); struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev); +void msm_fbdev_free(struct drm_device *dev); struct hdmi; -int hdmi_modeset_init(struct hdmi *hdmi, struct drm_device *dev, +int msm_hdmi_modeset_init(struct hdmi *hdmi, struct drm_device *dev, struct drm_encoder *encoder); -void __init hdmi_register(void); -void __exit hdmi_unregister(void); +void __init msm_hdmi_register(void); +void __exit msm_hdmi_unregister(void); struct msm_edp; void __init msm_edp_register(void); diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 6d7cd3fe21e7..43d2181231c0 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -323,28 +323,27 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, struct drm_msm_gem_submit *args = data; struct msm_file_private *ctx = file->driver_priv; struct msm_gem_submit *submit; - struct msm_gpu *gpu; + struct msm_gpu *gpu = priv->gpu; unsigned i; int ret; + if (!gpu) + return -ENXIO; + /* for now, we just have 3d pipe.. eventually this would need to * be more clever to dispatch to appropriate gpu module: */ if (args->pipe != MSM_PIPE_3D0) return -EINVAL; - gpu = priv->gpu; - if (args->nr_cmds > MAX_CMDS) return -EINVAL; - mutex_lock(&dev->struct_mutex); - submit = submit_create(dev, gpu, args->nr_bos); - if (!submit) { - ret = -ENOMEM; - goto out; - } + if (!submit) + return -ENOMEM; + + mutex_lock(&dev->struct_mutex); ret = submit_lookup_objects(submit, args, file); if (ret) @@ -419,8 +418,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, args->fence = submit->fence; out: - if (submit) - submit_cleanup(submit, !!ret); + submit_cleanup(submit, !!ret); mutex_unlock(&dev->struct_mutex); return ret; } diff --git a/drivers/gpu/drm/msm/msm_iommu.c b/drivers/gpu/drm/msm/msm_iommu.c index 7ac2f1997e4a..a7a0b6d9b057 100644 --- a/drivers/gpu/drm/msm/msm_iommu.c +++ b/drivers/gpu/drm/msm/msm_iommu.c @@ -31,13 +31,15 @@ static int msm_fault_handler(struct iommu_domain *iommu, struct device *dev, return 0; } -static int msm_iommu_attach(struct msm_mmu *mmu, const char **names, int cnt) +static int msm_iommu_attach(struct msm_mmu *mmu, const char * const *names, + int cnt) { struct msm_iommu *iommu = to_msm_iommu(mmu); return iommu_attach_device(iommu->domain, mmu->dev); } -static void msm_iommu_detach(struct msm_mmu *mmu, const char **names, int cnt) +static void msm_iommu_detach(struct msm_mmu *mmu, const char * const *names, + int cnt) { struct msm_iommu *iommu = to_msm_iommu(mmu); iommu_detach_device(iommu->domain, mmu->dev); diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h index 7cd88d9dc155..b8ca9a0e9170 100644 --- a/drivers/gpu/drm/msm/msm_mmu.h +++ b/drivers/gpu/drm/msm/msm_mmu.h @@ -21,8 +21,8 @@ #include <linux/iommu.h> struct msm_mmu_funcs { - int (*attach)(struct msm_mmu *mmu, const char **names, int cnt); - void (*detach)(struct msm_mmu *mmu, const char **names, int cnt); + int (*attach)(struct msm_mmu *mmu, const char * const *names, int cnt); + void (*detach)(struct msm_mmu *mmu, const char * const *names, int cnt); int (*map)(struct msm_mmu *mmu, uint32_t iova, struct sg_table *sgt, unsigned len, int prot); int (*unmap)(struct msm_mmu *mmu, uint32_t iova, struct sg_table *sgt, diff --git a/drivers/gpu/drm/omapdrm/Kconfig b/drivers/gpu/drm/omapdrm/Kconfig index 336ad4de9981..73241c4eb7aa 100644 --- a/drivers/gpu/drm/omapdrm/Kconfig +++ b/drivers/gpu/drm/omapdrm/Kconfig @@ -2,7 +2,6 @@ config DRM_OMAP tristate "OMAP DRM" depends on DRM depends on ARCH_OMAP2PLUS || ARCH_MULTIPLATFORM - select OMAP2_DSS select DRM_KMS_HELPER select DRM_KMS_FB_HELPER select FB_SYS_FILLRECT diff --git a/drivers/gpu/drm/omapdrm/Makefile b/drivers/gpu/drm/omapdrm/Makefile index fe4c2228bc18..48b7b750c05c 100644 --- a/drivers/gpu/drm/omapdrm/Makefile +++ b/drivers/gpu/drm/omapdrm/Makefile @@ -6,7 +6,7 @@ obj-y += dss/ obj-y += displays/ -ccflags-y := -Iinclude/drm -Werror +ccflags-y := -Iinclude/drm omapdrm-y := omap_drv.o \ omap_irq.o \ omap_debugfs.o \ diff --git a/drivers/gpu/drm/omapdrm/displays/connector-dvi.c b/drivers/gpu/drm/omapdrm/displays/connector-dvi.c index d811e6dcaef7..3fd5a0b4d4cf 100644 --- a/drivers/gpu/drm/omapdrm/displays/connector-dvi.c +++ b/drivers/gpu/drm/omapdrm/displays/connector-dvi.c @@ -236,46 +236,6 @@ static struct omap_dss_driver dvic_driver = { .detect = dvic_detect, }; -static int dvic_probe_pdata(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct connector_dvi_platform_data *pdata; - struct omap_dss_device *in, *dssdev; - int i2c_bus_num; - - pdata = dev_get_platdata(&pdev->dev); - i2c_bus_num = pdata->i2c_bus_num; - - if (i2c_bus_num != -1) { - struct i2c_adapter *adapter; - - adapter = i2c_get_adapter(i2c_bus_num); - if (!adapter) { - dev_err(&pdev->dev, - "Failed to get I2C adapter, bus %d\n", - i2c_bus_num); - return -EPROBE_DEFER; - } - - ddata->i2c_adapter = adapter; - } - - in = omap_dss_find_output(pdata->source); - if (in == NULL) { - i2c_put_adapter(ddata->i2c_adapter); - - dev_err(&pdev->dev, "Failed to find video source\n"); - return -EPROBE_DEFER; - } - - ddata->in = in; - - dssdev = &ddata->dssdev; - dssdev->name = pdata->name; - - return 0; -} - static int dvic_probe_of(struct platform_device *pdev) { struct panel_drv_data *ddata = platform_get_drvdata(pdev); @@ -319,17 +279,12 @@ static int dvic_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - if (dev_get_platdata(&pdev->dev)) { - r = dvic_probe_pdata(pdev); - if (r) - return r; - } else if (pdev->dev.of_node) { - r = dvic_probe_of(pdev); - if (r) - return r; - } else { + if (pdev->dev.of_node) return -ENODEV; - } + + r = dvic_probe_of(pdev); + if (r) + return r; ddata->timings = dvic_default_timings; diff --git a/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c b/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c index 6ee4129bc0c0..225fd8d6ab31 100644 --- a/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c +++ b/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c @@ -206,30 +206,6 @@ static struct omap_dss_driver hdmic_driver = { .set_hdmi_infoframe = hdmic_set_infoframe, }; -static int hdmic_probe_pdata(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct connector_hdmi_platform_data *pdata; - struct omap_dss_device *in, *dssdev; - - pdata = dev_get_platdata(&pdev->dev); - - ddata->hpd_gpio = -ENODEV; - - in = omap_dss_find_output(pdata->source); - if (in == NULL) { - dev_err(&pdev->dev, "Failed to find video source\n"); - return -EPROBE_DEFER; - } - - ddata->in = in; - - dssdev = &ddata->dssdev; - dssdev->name = pdata->name; - - return 0; -} - static int hdmic_probe_of(struct platform_device *pdev) { struct panel_drv_data *ddata = platform_get_drvdata(pdev); @@ -268,17 +244,12 @@ static int hdmic_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); ddata->dev = &pdev->dev; - if (dev_get_platdata(&pdev->dev)) { - r = hdmic_probe_pdata(pdev); - if (r) - return r; - } else if (pdev->dev.of_node) { - r = hdmic_probe_of(pdev); - if (r) - return r; - } else { + if (!pdev->dev.of_node) return -ENODEV; - } + + r = hdmic_probe_of(pdev); + if (r) + return r; if (gpio_is_valid(ddata->hpd_gpio)) { r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio, diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c b/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c index d9048b3df495..2fd5602880a7 100644 --- a/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c +++ b/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c @@ -166,32 +166,6 @@ static const struct omapdss_dvi_ops tfp410_dvi_ops = { .get_timings = tfp410_get_timings, }; -static int tfp410_probe_pdata(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct encoder_tfp410_platform_data *pdata; - struct omap_dss_device *dssdev, *in; - - pdata = dev_get_platdata(&pdev->dev); - - ddata->pd_gpio = pdata->power_down_gpio; - - ddata->data_lines = pdata->data_lines; - - in = omap_dss_find_output(pdata->source); - if (in == NULL) { - dev_err(&pdev->dev, "Failed to find video source\n"); - return -ENODEV; - } - - ddata->in = in; - - dssdev = &ddata->dssdev; - dssdev->name = pdata->name; - - return 0; -} - static int tfp410_probe_of(struct platform_device *pdev) { struct panel_drv_data *ddata = platform_get_drvdata(pdev); @@ -231,17 +205,12 @@ static int tfp410_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - if (dev_get_platdata(&pdev->dev)) { - r = tfp410_probe_pdata(pdev); - if (r) - return r; - } else if (pdev->dev.of_node) { - r = tfp410_probe_of(pdev); - if (r) - return r; - } else { + if (!pdev->dev.of_node) return -ENODEV; - } + + r = tfp410_probe_of(pdev); + if (r) + return r; if (gpio_is_valid(ddata->pd_gpio)) { r = devm_gpio_request_one(&pdev->dev, ddata->pd_gpio, diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c b/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c index 990af6baeb0f..916a89978387 100644 --- a/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c +++ b/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c @@ -13,9 +13,8 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/gpio.h> #include <linux/platform_device.h> -#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> #include <video/omapdss.h> #include <video/omap-panel-data.h> @@ -24,9 +23,9 @@ struct panel_drv_data { struct omap_dss_device dssdev; struct omap_dss_device *in; - int ct_cp_hpd_gpio; - int ls_oe_gpio; - int hpd_gpio; + struct gpio_desc *ct_cp_hpd_gpio; + struct gpio_desc *ls_oe_gpio; + struct gpio_desc *hpd_gpio; struct omap_video_timings timings; }; @@ -47,7 +46,7 @@ static int tpd_connect(struct omap_dss_device *dssdev, dst->src = dssdev; dssdev->dst = dst; - gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1); + gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1); /* DC-DC converter needs at max 300us to get to 90% of 5V */ udelay(300); @@ -65,7 +64,7 @@ static void tpd_disconnect(struct omap_dss_device *dssdev, if (dst != dssdev->dst) return; - gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0); + gpiod_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0); dst->src = NULL; dssdev->dst = NULL; @@ -145,16 +144,14 @@ static int tpd_read_edid(struct omap_dss_device *dssdev, struct omap_dss_device *in = ddata->in; int r; - if (!gpio_get_value_cansleep(ddata->hpd_gpio)) + if (!gpiod_get_value_cansleep(ddata->hpd_gpio)) return -ENODEV; - if (gpio_is_valid(ddata->ls_oe_gpio)) - gpio_set_value_cansleep(ddata->ls_oe_gpio, 1); + gpiod_set_value_cansleep(ddata->ls_oe_gpio, 1); r = in->ops.hdmi->read_edid(in, edid, len); - if (gpio_is_valid(ddata->ls_oe_gpio)) - gpio_set_value_cansleep(ddata->ls_oe_gpio, 0); + gpiod_set_value_cansleep(ddata->ls_oe_gpio, 0); return r; } @@ -163,7 +160,7 @@ static bool tpd_detect(struct omap_dss_device *dssdev) { struct panel_drv_data *ddata = to_panel_data(dssdev); - return gpio_get_value_cansleep(ddata->hpd_gpio); + return gpiod_get_value_cansleep(ddata->hpd_gpio); } static int tpd_set_infoframe(struct omap_dss_device *dssdev, @@ -201,63 +198,11 @@ static const struct omapdss_hdmi_ops tpd_hdmi_ops = { .set_hdmi_mode = tpd_set_hdmi_mode, }; -static int tpd_probe_pdata(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct encoder_tpd12s015_platform_data *pdata; - struct omap_dss_device *dssdev, *in; - - pdata = dev_get_platdata(&pdev->dev); - - ddata->ct_cp_hpd_gpio = pdata->ct_cp_hpd_gpio; - ddata->ls_oe_gpio = pdata->ls_oe_gpio; - ddata->hpd_gpio = pdata->hpd_gpio; - - in = omap_dss_find_output(pdata->source); - if (in == NULL) { - dev_err(&pdev->dev, "Failed to find video source\n"); - return -ENODEV; - } - - ddata->in = in; - - dssdev = &ddata->dssdev; - dssdev->name = pdata->name; - - return 0; -} - static int tpd_probe_of(struct platform_device *pdev) { struct panel_drv_data *ddata = platform_get_drvdata(pdev); struct device_node *node = pdev->dev.of_node; struct omap_dss_device *in; - int gpio; - - /* CT CP HPD GPIO */ - gpio = of_get_gpio(node, 0); - if (!gpio_is_valid(gpio)) { - dev_err(&pdev->dev, "failed to parse CT CP HPD gpio\n"); - return gpio; - } - ddata->ct_cp_hpd_gpio = gpio; - - /* LS OE GPIO */ - gpio = of_get_gpio(node, 1); - if (gpio_is_valid(gpio) || gpio == -ENOENT) { - ddata->ls_oe_gpio = gpio; - } else { - dev_err(&pdev->dev, "failed to parse LS OE gpio\n"); - return gpio; - } - - /* HPD GPIO */ - gpio = of_get_gpio(node, 2); - if (!gpio_is_valid(gpio)) { - dev_err(&pdev->dev, "failed to parse HPD gpio\n"); - return gpio; - } - ddata->hpd_gpio = gpio; in = omapdss_of_find_source_for_first_ep(node); if (IS_ERR(in)) { @@ -275,6 +220,7 @@ static int tpd_probe(struct platform_device *pdev) struct omap_dss_device *in, *dssdev; struct panel_drv_data *ddata; int r; + struct gpio_desc *gpio; ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); if (!ddata) @@ -282,35 +228,35 @@ static int tpd_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - if (dev_get_platdata(&pdev->dev)) { - r = tpd_probe_pdata(pdev); - if (r) - return r; - } else if (pdev->dev.of_node) { - r = tpd_probe_of(pdev); - if (r) - return r; - } else { + if (!pdev->dev.of_node) return -ENODEV; - } - r = devm_gpio_request_one(&pdev->dev, ddata->ct_cp_hpd_gpio, - GPIOF_OUT_INIT_LOW, "hdmi_ct_cp_hpd"); + r = tpd_probe_of(pdev); if (r) + return r; + + + gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0, + GPIOD_OUT_LOW); + if (IS_ERR(gpio)) goto err_gpio; - if (gpio_is_valid(ddata->ls_oe_gpio)) { - r = devm_gpio_request_one(&pdev->dev, ddata->ls_oe_gpio, - GPIOF_OUT_INIT_LOW, "hdmi_ls_oe"); - if (r) - goto err_gpio; - } + ddata->ct_cp_hpd_gpio = gpio; - r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio, - GPIOF_DIR_IN, "hdmi_hpd"); - if (r) + gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1, + GPIOD_OUT_LOW); + if (IS_ERR(gpio)) + goto err_gpio; + + ddata->ls_oe_gpio = gpio; + + gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2, + GPIOD_IN); + if (IS_ERR(gpio)) goto err_gpio; + ddata->hpd_gpio = gpio; + dssdev = &ddata->dssdev; dssdev->ops.hdmi = &tpd_hdmi_ops; dssdev->dev = &pdev->dev; diff --git a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c index 3414c2609320..ae7dd625e19f 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c @@ -1127,40 +1127,6 @@ static struct omap_dss_driver dsicm_ops = { .memory_read = dsicm_memory_read, }; -static int dsicm_probe_pdata(struct platform_device *pdev) -{ - const struct panel_dsicm_platform_data *pdata; - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *dssdev, *in; - - pdata = dev_get_platdata(&pdev->dev); - - in = omap_dss_find_output(pdata->source); - if (in == NULL) { - dev_err(&pdev->dev, "failed to find video source\n"); - return -EPROBE_DEFER; - } - ddata->in = in; - - ddata->reset_gpio = pdata->reset_gpio; - - if (pdata->use_ext_te) - ddata->ext_te_gpio = pdata->ext_te_gpio; - else - ddata->ext_te_gpio = -1; - - ddata->ulps_timeout = pdata->ulps_timeout; - - ddata->use_dsi_backlight = pdata->use_dsi_backlight; - - ddata->pin_config = pdata->pin_config; - - dssdev = &ddata->dssdev; - dssdev->name = pdata->name; - - return 0; -} - static int dsicm_probe_of(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; @@ -1214,17 +1180,12 @@ static int dsicm_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); ddata->pdev = pdev; - if (dev_get_platdata(dev)) { - r = dsicm_probe_pdata(pdev); - if (r) - return r; - } else if (pdev->dev.of_node) { - r = dsicm_probe_of(pdev); - if (r) - return r; - } else { + if (pdev->dev.of_node) return -ENODEV; - } + + r = dsicm_probe_of(pdev); + if (r) + return r; ddata->timings.x_res = 864; ddata->timings.y_res = 480; diff --git a/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c b/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c index 18eb60e9c9ec..458f77bc473d 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c @@ -240,44 +240,6 @@ static struct omap_dss_driver lb035q02_ops = { .get_resolution = omapdss_default_get_resolution, }; -static int lb035q02_probe_pdata(struct spi_device *spi) -{ - const struct panel_lb035q02_platform_data *pdata; - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct omap_dss_device *dssdev, *in; - int r; - - pdata = dev_get_platdata(&spi->dev); - - in = omap_dss_find_output(pdata->source); - if (in == NULL) { - dev_err(&spi->dev, "failed to find video source '%s'\n", - pdata->source); - return -EPROBE_DEFER; - } - - ddata->in = in; - - ddata->data_lines = pdata->data_lines; - - dssdev = &ddata->dssdev; - dssdev->name = pdata->name; - - r = devm_gpio_request_one(&spi->dev, pdata->enable_gpio, - GPIOF_OUT_INIT_LOW, "panel enable"); - if (r) - goto err_gpio; - - ddata->enable_gpio = gpio_to_desc(pdata->enable_gpio); - - ddata->backlight_gpio = pdata->backlight_gpio; - - return 0; -err_gpio: - omap_dss_put_device(ddata->in); - return r; -} - static int lb035q02_probe_of(struct spi_device *spi) { struct device_node *node = spi->dev.of_node; @@ -320,17 +282,12 @@ static int lb035q02_panel_spi_probe(struct spi_device *spi) ddata->spi = spi; - if (dev_get_platdata(&spi->dev)) { - r = lb035q02_probe_pdata(spi); - if (r) - return r; - } else if (spi->dev.of_node) { - r = lb035q02_probe_of(spi); - if (r) - return r; - } else { + if (!spi->dev.of_node) return -ENODEV; - } + + r = lb035q02_probe_of(spi); + if (r) + return r; if (gpio_is_valid(ddata->backlight_gpio)) { r = devm_gpio_request_one(&spi->dev, ddata->backlight_gpio, diff --git a/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c b/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c index 8a928c9a2fc9..780cb263a318 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c @@ -19,7 +19,6 @@ #include <linux/of_gpio.h> #include <video/omapdss.h> -#include <video/omap-panel-data.h> struct panel_drv_data { struct omap_dss_device dssdev; @@ -232,34 +231,6 @@ static struct omap_dss_driver nec_8048_ops = { .get_resolution = omapdss_default_get_resolution, }; - -static int nec_8048_probe_pdata(struct spi_device *spi) -{ - const struct panel_nec_nl8048hl11_platform_data *pdata; - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct omap_dss_device *dssdev, *in; - - pdata = dev_get_platdata(&spi->dev); - - ddata->qvga_gpio = pdata->qvga_gpio; - ddata->res_gpio = pdata->res_gpio; - - in = omap_dss_find_output(pdata->source); - if (in == NULL) { - dev_err(&spi->dev, "failed to find video source '%s'\n", - pdata->source); - return -EPROBE_DEFER; - } - ddata->in = in; - - ddata->data_lines = pdata->data_lines; - - dssdev = &ddata->dssdev; - dssdev->name = pdata->name; - - return 0; -} - static int nec_8048_probe_of(struct spi_device *spi) { struct device_node *node = spi->dev.of_node; @@ -315,17 +286,12 @@ static int nec_8048_probe(struct spi_device *spi) ddata->spi = spi; - if (dev_get_platdata(&spi->dev)) { - r = nec_8048_probe_pdata(spi); - if (r) - return r; - } else if (spi->dev.of_node) { - r = nec_8048_probe_of(spi); - if (r) - return r; - } else { + if (!spi->dev.of_node) return -ENODEV; - } + + r = nec_8048_probe_of(spi); + if (r) + return r; if (gpio_is_valid(ddata->qvga_gpio)) { r = devm_gpio_request_one(&spi->dev, ddata->qvga_gpio, diff --git a/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c b/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c index abfd1f6e3327..529a017602e4 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c @@ -18,7 +18,6 @@ #include <linux/slab.h> #include <linux/regulator/consumer.h> #include <video/omapdss.h> -#include <video/omap-panel-data.h> struct panel_drv_data { struct omap_dss_device dssdev; @@ -197,73 +196,6 @@ static struct omap_dss_driver sharp_ls_ops = { .get_resolution = omapdss_default_get_resolution, }; -static int sharp_ls_get_gpio(struct device *dev, int gpio, unsigned long flags, - char *desc, struct gpio_desc **gpiod) -{ - struct gpio_desc *gd; - int r; - - *gpiod = NULL; - - r = devm_gpio_request_one(dev, gpio, flags, desc); - if (r) - return r == -ENOENT ? 0 : r; - - gd = gpio_to_desc(gpio); - if (IS_ERR(gd)) - return PTR_ERR(gd) == -ENOENT ? 0 : PTR_ERR(gd); - - *gpiod = gd; - return 0; -} - -static int sharp_ls_probe_pdata(struct platform_device *pdev) -{ - const struct panel_sharp_ls037v7dw01_platform_data *pdata; - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *dssdev, *in; - int r; - - pdata = dev_get_platdata(&pdev->dev); - - in = omap_dss_find_output(pdata->source); - if (in == NULL) { - dev_err(&pdev->dev, "failed to find video source '%s'\n", - pdata->source); - return -EPROBE_DEFER; - } - - ddata->in = in; - - ddata->data_lines = pdata->data_lines; - - dssdev = &ddata->dssdev; - dssdev->name = pdata->name; - - r = sharp_ls_get_gpio(&pdev->dev, pdata->mo_gpio, GPIOF_OUT_INIT_LOW, - "lcd MO", &ddata->mo_gpio); - if (r) - return r; - r = sharp_ls_get_gpio(&pdev->dev, pdata->lr_gpio, GPIOF_OUT_INIT_HIGH, - "lcd LR", &ddata->lr_gpio); - if (r) - return r; - r = sharp_ls_get_gpio(&pdev->dev, pdata->ud_gpio, GPIOF_OUT_INIT_HIGH, - "lcd UD", &ddata->ud_gpio); - if (r) - return r; - r = sharp_ls_get_gpio(&pdev->dev, pdata->resb_gpio, GPIOF_OUT_INIT_LOW, - "lcd RESB", &ddata->resb_gpio); - if (r) - return r; - r = sharp_ls_get_gpio(&pdev->dev, pdata->ini_gpio, GPIOF_OUT_INIT_LOW, - "lcd INI", &ddata->ini_gpio); - if (r) - return r; - - return 0; -} - static int sharp_ls_get_gpio_of(struct device *dev, int index, int val, const char *desc, struct gpio_desc **gpiod) { @@ -340,17 +272,12 @@ static int sharp_ls_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - if (dev_get_platdata(&pdev->dev)) { - r = sharp_ls_probe_pdata(pdev); - if (r) - return r; - } else if (pdev->dev.of_node) { - r = sharp_ls_probe_of(pdev); - if (r) - return r; - } else { + if (!pdev->dev.of_node) return -ENODEV; - } + + r = sharp_ls_probe_of(pdev); + if (r) + return r; ddata->videomode = sharp_ls_timings; diff --git a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c index 4d657f3ab679..bd8d85041926 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c @@ -29,7 +29,6 @@ #include <linux/spi/spi.h> #include <linux/gpio.h> #include <video/omapdss.h> -#include <video/omap-panel-data.h> struct panel_drv_data { struct omap_dss_device dssdev; @@ -365,31 +364,6 @@ static struct omap_dss_driver td028ttec1_ops = { .check_timings = td028ttec1_panel_check_timings, }; -static int td028ttec1_panel_probe_pdata(struct spi_device *spi) -{ - const struct panel_tpo_td028ttec1_platform_data *pdata; - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct omap_dss_device *dssdev, *in; - - pdata = dev_get_platdata(&spi->dev); - - in = omap_dss_find_output(pdata->source); - if (in == NULL) { - dev_err(&spi->dev, "failed to find video source '%s'\n", - pdata->source); - return -EPROBE_DEFER; - } - - ddata->in = in; - - ddata->data_lines = pdata->data_lines; - - dssdev = &ddata->dssdev; - dssdev->name = pdata->name; - - return 0; -} - static int td028ttec1_probe_of(struct spi_device *spi) { struct device_node *node = spi->dev.of_node; @@ -432,17 +406,12 @@ static int td028ttec1_panel_probe(struct spi_device *spi) ddata->spi_dev = spi; - if (dev_get_platdata(&spi->dev)) { - r = td028ttec1_panel_probe_pdata(spi); - if (r) - return r; - } else if (spi->dev.of_node) { - r = td028ttec1_probe_of(spi); - if (r) - return r; - } else { + if (!spi->dev.of_node) return -ENODEV; - } + + r = td028ttec1_probe_of(spi); + if (r) + return r; ddata->videomode = td028ttec1_panel_timings; diff --git a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td043mtea1.c b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td043mtea1.c index 68e3b68a2920..03e2beb7b4f0 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td043mtea1.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td043mtea1.c @@ -20,7 +20,6 @@ #include <linux/of_gpio.h> #include <video/omapdss.h> -#include <video/omap-panel-data.h> #define TPO_R02_MODE(x) ((x) & 7) #define TPO_R02_MODE_800x480 7 @@ -464,33 +463,6 @@ static struct omap_dss_driver tpo_td043_ops = { .get_resolution = omapdss_default_get_resolution, }; - -static int tpo_td043_probe_pdata(struct spi_device *spi) -{ - const struct panel_tpo_td043mtea1_platform_data *pdata; - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct omap_dss_device *dssdev, *in; - - pdata = dev_get_platdata(&spi->dev); - - ddata->nreset_gpio = pdata->nreset_gpio; - - in = omap_dss_find_output(pdata->source); - if (in == NULL) { - dev_err(&spi->dev, "failed to find video source '%s'\n", - pdata->source); - return -EPROBE_DEFER; - } - ddata->in = in; - - ddata->data_lines = pdata->data_lines; - - dssdev = &ddata->dssdev; - dssdev->name = pdata->name; - - return 0; -} - static int tpo_td043_probe_of(struct spi_device *spi) { struct device_node *node = spi->dev.of_node; @@ -541,17 +513,12 @@ static int tpo_td043_probe(struct spi_device *spi) ddata->spi = spi; - if (dev_get_platdata(&spi->dev)) { - r = tpo_td043_probe_pdata(spi); - if (r) - return r; - } else if (spi->dev.of_node) { - r = tpo_td043_probe_of(spi); - if (r) - return r; - } else { + if (!spi->dev.of_node) return -ENODEV; - } + + r = tpo_td043_probe_of(spi); + if (r) + return r; ddata->mode = TPO_R02_MODE_800x480; memcpy(ddata->gamma, tpo_td043_def_gamma, sizeof(ddata->gamma)); diff --git a/drivers/gpu/drm/omapdrm/dss/Makefile b/drivers/gpu/drm/omapdrm/dss/Makefile index b5136d3d4b77..b651ec9751e6 100644 --- a/drivers/gpu/drm/omapdrm/dss/Makefile +++ b/drivers/gpu/drm/omapdrm/dss/Makefile @@ -3,9 +3,6 @@ obj-$(CONFIG_OMAP2_DSS) += omapdss.o # Core DSS files omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \ output.o dss-of.o pll.o video-pll.o -# DSS compat layer files -omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \ - dispc-compat.o display-sysfs.o omapdss-$(CONFIG_OMAP2_DSS_DPI) += dpi.o omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o diff --git a/drivers/gpu/drm/omapdrm/dss/apply.c b/drivers/gpu/drm/omapdrm/dss/apply.c deleted file mode 100644 index 663ccc3bf4e5..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/apply.c +++ /dev/null @@ -1,1702 +0,0 @@ -/* - * Copyright (C) 2011 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#define DSS_SUBSYS_NAME "APPLY" - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/jiffies.h> - -#include <video/omapdss.h> - -#include "dss.h" -#include "dss_features.h" -#include "dispc-compat.h" - -/* - * We have 4 levels of cache for the dispc settings. First two are in SW and - * the latter two in HW. - * - * set_info() - * v - * +--------------------+ - * | user_info | - * +--------------------+ - * v - * apply() - * v - * +--------------------+ - * | info | - * +--------------------+ - * v - * write_regs() - * v - * +--------------------+ - * | shadow registers | - * +--------------------+ - * v - * VFP or lcd/digit_enable - * v - * +--------------------+ - * | registers | - * +--------------------+ - */ - -struct ovl_priv_data { - - bool user_info_dirty; - struct omap_overlay_info user_info; - - bool info_dirty; - struct omap_overlay_info info; - - bool shadow_info_dirty; - - bool extra_info_dirty; - bool shadow_extra_info_dirty; - - bool enabled; - u32 fifo_low, fifo_high; - - /* - * True if overlay is to be enabled. Used to check and calculate configs - * for the overlay before it is enabled in the HW. - */ - bool enabling; -}; - -struct mgr_priv_data { - - bool user_info_dirty; - struct omap_overlay_manager_info user_info; - - bool info_dirty; - struct omap_overlay_manager_info info; - - bool shadow_info_dirty; - - /* If true, GO bit is up and shadow registers cannot be written. - * Never true for manual update displays */ - bool busy; - - /* If true, dispc output is enabled */ - bool updating; - - /* If true, a display is enabled using this manager */ - bool enabled; - - bool extra_info_dirty; - bool shadow_extra_info_dirty; - - struct omap_video_timings timings; - struct dss_lcd_mgr_config lcd_config; - - void (*framedone_handler)(void *); - void *framedone_handler_data; -}; - -static struct { - struct ovl_priv_data ovl_priv_data_array[MAX_DSS_OVERLAYS]; - struct mgr_priv_data mgr_priv_data_array[MAX_DSS_MANAGERS]; - - bool irq_enabled; -} dss_data; - -/* protects dss_data */ -static spinlock_t data_lock; -/* lock for blocking functions */ -static DEFINE_MUTEX(apply_lock); -static DECLARE_COMPLETION(extra_updated_completion); - -static void dss_register_vsync_isr(void); - -static struct ovl_priv_data *get_ovl_priv(struct omap_overlay *ovl) -{ - return &dss_data.ovl_priv_data_array[ovl->id]; -} - -static struct mgr_priv_data *get_mgr_priv(struct omap_overlay_manager *mgr) -{ - return &dss_data.mgr_priv_data_array[mgr->id]; -} - -static void apply_init_priv(void) -{ - const int num_ovls = dss_feat_get_num_ovls(); - struct mgr_priv_data *mp; - int i; - - spin_lock_init(&data_lock); - - for (i = 0; i < num_ovls; ++i) { - struct ovl_priv_data *op; - - op = &dss_data.ovl_priv_data_array[i]; - - op->info.color_mode = OMAP_DSS_COLOR_RGB16; - op->info.rotation_type = OMAP_DSS_ROT_DMA; - - op->info.global_alpha = 255; - - switch (i) { - case 0: - op->info.zorder = 0; - break; - case 1: - op->info.zorder = - dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 3 : 0; - break; - case 2: - op->info.zorder = - dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 2 : 0; - break; - case 3: - op->info.zorder = - dss_has_feature(FEAT_ALPHA_FREE_ZORDER) ? 1 : 0; - break; - } - - op->user_info = op->info; - } - - /* - * Initialize some of the lcd_config fields for TV manager, this lets - * us prevent checking if the manager is LCD or TV at some places - */ - mp = &dss_data.mgr_priv_data_array[OMAP_DSS_CHANNEL_DIGIT]; - - mp->lcd_config.video_port_width = 24; - mp->lcd_config.clock_info.lck_div = 1; - mp->lcd_config.clock_info.pck_div = 1; -} - -/* - * A LCD manager's stallmode decides whether it is in manual or auto update. TV - * manager is always auto update, stallmode field for TV manager is false by - * default - */ -static bool ovl_manual_update(struct omap_overlay *ovl) -{ - struct mgr_priv_data *mp = get_mgr_priv(ovl->manager); - - return mp->lcd_config.stallmode; -} - -static bool mgr_manual_update(struct omap_overlay_manager *mgr) -{ - struct mgr_priv_data *mp = get_mgr_priv(mgr); - - return mp->lcd_config.stallmode; -} - -static int dss_check_settings_low(struct omap_overlay_manager *mgr, - bool applying) -{ - struct omap_overlay_info *oi; - struct omap_overlay_manager_info *mi; - struct omap_overlay *ovl; - struct omap_overlay_info *ois[MAX_DSS_OVERLAYS]; - struct ovl_priv_data *op; - struct mgr_priv_data *mp; - - mp = get_mgr_priv(mgr); - - if (!mp->enabled) - return 0; - - if (applying && mp->user_info_dirty) - mi = &mp->user_info; - else - mi = &mp->info; - - /* collect the infos to be tested into the array */ - list_for_each_entry(ovl, &mgr->overlays, list) { - op = get_ovl_priv(ovl); - - if (!op->enabled && !op->enabling) - oi = NULL; - else if (applying && op->user_info_dirty) - oi = &op->user_info; - else - oi = &op->info; - - ois[ovl->id] = oi; - } - - return dss_mgr_check(mgr, mi, &mp->timings, &mp->lcd_config, ois); -} - -/* - * check manager and overlay settings using overlay_info from data->info - */ -static int dss_check_settings(struct omap_overlay_manager *mgr) -{ - return dss_check_settings_low(mgr, false); -} - -/* - * check manager and overlay settings using overlay_info from ovl->info if - * dirty and from data->info otherwise - */ -static int dss_check_settings_apply(struct omap_overlay_manager *mgr) -{ - return dss_check_settings_low(mgr, true); -} - -static bool need_isr(void) -{ - const int num_mgrs = dss_feat_get_num_mgrs(); - int i; - - for (i = 0; i < num_mgrs; ++i) { - struct omap_overlay_manager *mgr; - struct mgr_priv_data *mp; - struct omap_overlay *ovl; - - mgr = omap_dss_get_overlay_manager(i); - mp = get_mgr_priv(mgr); - - if (!mp->enabled) - continue; - - if (mgr_manual_update(mgr)) { - /* to catch FRAMEDONE */ - if (mp->updating) - return true; - } else { - /* to catch GO bit going down */ - if (mp->busy) - return true; - - /* to write new values to registers */ - if (mp->info_dirty) - return true; - - /* to set GO bit */ - if (mp->shadow_info_dirty) - return true; - - /* - * NOTE: we don't check extra_info flags for disabled - * managers, once the manager is enabled, the extra_info - * related manager changes will be taken in by HW. - */ - - /* to write new values to registers */ - if (mp->extra_info_dirty) - return true; - - /* to set GO bit */ - if (mp->shadow_extra_info_dirty) - return true; - - list_for_each_entry(ovl, &mgr->overlays, list) { - struct ovl_priv_data *op; - - op = get_ovl_priv(ovl); - - /* - * NOTE: we check extra_info flags even for - * disabled overlays, as extra_infos need to be - * always written. - */ - - /* to write new values to registers */ - if (op->extra_info_dirty) - return true; - - /* to set GO bit */ - if (op->shadow_extra_info_dirty) - return true; - - if (!op->enabled) - continue; - - /* to write new values to registers */ - if (op->info_dirty) - return true; - - /* to set GO bit */ - if (op->shadow_info_dirty) - return true; - } - } - } - - return false; -} - -static bool need_go(struct omap_overlay_manager *mgr) -{ - struct omap_overlay *ovl; - struct mgr_priv_data *mp; - struct ovl_priv_data *op; - - mp = get_mgr_priv(mgr); - - if (mp->shadow_info_dirty || mp->shadow_extra_info_dirty) - return true; - - list_for_each_entry(ovl, &mgr->overlays, list) { - op = get_ovl_priv(ovl); - if (op->shadow_info_dirty || op->shadow_extra_info_dirty) - return true; - } - - return false; -} - -/* returns true if an extra_info field is currently being updated */ -static bool extra_info_update_ongoing(void) -{ - const int num_mgrs = dss_feat_get_num_mgrs(); - int i; - - for (i = 0; i < num_mgrs; ++i) { - struct omap_overlay_manager *mgr; - struct omap_overlay *ovl; - struct mgr_priv_data *mp; - - mgr = omap_dss_get_overlay_manager(i); - mp = get_mgr_priv(mgr); - - if (!mp->enabled) - continue; - - if (!mp->updating) - continue; - - if (mp->extra_info_dirty || mp->shadow_extra_info_dirty) - return true; - - list_for_each_entry(ovl, &mgr->overlays, list) { - struct ovl_priv_data *op = get_ovl_priv(ovl); - - if (op->extra_info_dirty || op->shadow_extra_info_dirty) - return true; - } - } - - return false; -} - -/* wait until no extra_info updates are pending */ -static void wait_pending_extra_info_updates(void) -{ - bool updating; - unsigned long flags; - unsigned long t; - int r; - - spin_lock_irqsave(&data_lock, flags); - - updating = extra_info_update_ongoing(); - - if (!updating) { - spin_unlock_irqrestore(&data_lock, flags); - return; - } - - init_completion(&extra_updated_completion); - - spin_unlock_irqrestore(&data_lock, flags); - - t = msecs_to_jiffies(500); - r = wait_for_completion_timeout(&extra_updated_completion, t); - if (r == 0) - DSSWARN("timeout in wait_pending_extra_info_updates\n"); -} - -static struct omap_dss_device *dss_mgr_get_device(struct omap_overlay_manager *mgr) -{ - struct omap_dss_device *dssdev; - - dssdev = mgr->output; - if (dssdev == NULL) - return NULL; - - while (dssdev->dst) - dssdev = dssdev->dst; - - if (dssdev->driver) - return dssdev; - else - return NULL; -} - -static struct omap_dss_device *dss_ovl_get_device(struct omap_overlay *ovl) -{ - return ovl->manager ? dss_mgr_get_device(ovl->manager) : NULL; -} - -static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr) -{ - unsigned long timeout = msecs_to_jiffies(500); - u32 irq; - int r; - - if (mgr->output == NULL) - return -ENODEV; - - r = dispc_runtime_get(); - if (r) - return r; - - switch (mgr->output->id) { - case OMAP_DSS_OUTPUT_VENC: - irq = DISPC_IRQ_EVSYNC_ODD; - break; - case OMAP_DSS_OUTPUT_HDMI: - irq = DISPC_IRQ_EVSYNC_EVEN; - break; - default: - irq = dispc_mgr_get_vsync_irq(mgr->id); - break; - } - - r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); - - dispc_runtime_put(); - - return r; -} - -static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) -{ - unsigned long timeout = msecs_to_jiffies(500); - struct mgr_priv_data *mp = get_mgr_priv(mgr); - u32 irq; - unsigned long flags; - int r; - int i; - - spin_lock_irqsave(&data_lock, flags); - - if (mgr_manual_update(mgr)) { - spin_unlock_irqrestore(&data_lock, flags); - return 0; - } - - if (!mp->enabled) { - spin_unlock_irqrestore(&data_lock, flags); - return 0; - } - - spin_unlock_irqrestore(&data_lock, flags); - - r = dispc_runtime_get(); - if (r) - return r; - - irq = dispc_mgr_get_vsync_irq(mgr->id); - - i = 0; - while (1) { - bool shadow_dirty, dirty; - - spin_lock_irqsave(&data_lock, flags); - dirty = mp->info_dirty; - shadow_dirty = mp->shadow_info_dirty; - spin_unlock_irqrestore(&data_lock, flags); - - if (!dirty && !shadow_dirty) { - r = 0; - break; - } - - /* 4 iterations is the worst case: - * 1 - initial iteration, dirty = true (between VFP and VSYNC) - * 2 - first VSYNC, dirty = true - * 3 - dirty = false, shadow_dirty = true - * 4 - shadow_dirty = false */ - if (i++ == 3) { - DSSERR("mgr(%d)->wait_for_go() not finishing\n", - mgr->id); - r = 0; - break; - } - - r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); - if (r == -ERESTARTSYS) - break; - - if (r) { - DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id); - break; - } - } - - dispc_runtime_put(); - - return r; -} - -static int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) -{ - unsigned long timeout = msecs_to_jiffies(500); - struct ovl_priv_data *op; - struct mgr_priv_data *mp; - u32 irq; - unsigned long flags; - int r; - int i; - - if (!ovl->manager) - return 0; - - mp = get_mgr_priv(ovl->manager); - - spin_lock_irqsave(&data_lock, flags); - - if (ovl_manual_update(ovl)) { - spin_unlock_irqrestore(&data_lock, flags); - return 0; - } - - if (!mp->enabled) { - spin_unlock_irqrestore(&data_lock, flags); - return 0; - } - - spin_unlock_irqrestore(&data_lock, flags); - - r = dispc_runtime_get(); - if (r) - return r; - - irq = dispc_mgr_get_vsync_irq(ovl->manager->id); - - op = get_ovl_priv(ovl); - i = 0; - while (1) { - bool shadow_dirty, dirty; - - spin_lock_irqsave(&data_lock, flags); - dirty = op->info_dirty; - shadow_dirty = op->shadow_info_dirty; - spin_unlock_irqrestore(&data_lock, flags); - - if (!dirty && !shadow_dirty) { - r = 0; - break; - } - - /* 4 iterations is the worst case: - * 1 - initial iteration, dirty = true (between VFP and VSYNC) - * 2 - first VSYNC, dirty = true - * 3 - dirty = false, shadow_dirty = true - * 4 - shadow_dirty = false */ - if (i++ == 3) { - DSSERR("ovl(%d)->wait_for_go() not finishing\n", - ovl->id); - r = 0; - break; - } - - r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); - if (r == -ERESTARTSYS) - break; - - if (r) { - DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id); - break; - } - } - - dispc_runtime_put(); - - return r; -} - -static void dss_ovl_write_regs(struct omap_overlay *ovl) -{ - struct ovl_priv_data *op = get_ovl_priv(ovl); - struct omap_overlay_info *oi; - bool replication; - struct mgr_priv_data *mp; - int r; - - DSSDBG("writing ovl %d regs\n", ovl->id); - - if (!op->enabled || !op->info_dirty) - return; - - oi = &op->info; - - mp = get_mgr_priv(ovl->manager); - - replication = dss_ovl_use_replication(mp->lcd_config, oi->color_mode); - - r = dispc_ovl_setup(ovl->id, oi, replication, &mp->timings, false); - if (r) { - /* - * We can't do much here, as this function can be called from - * vsync interrupt. - */ - DSSERR("dispc_ovl_setup failed for ovl %d\n", ovl->id); - - /* This will leave fifo configurations in a nonoptimal state */ - op->enabled = false; - dispc_ovl_enable(ovl->id, false); - return; - } - - op->info_dirty = false; - if (mp->updating) - op->shadow_info_dirty = true; -} - -static void dss_ovl_write_regs_extra(struct omap_overlay *ovl) -{ - struct ovl_priv_data *op = get_ovl_priv(ovl); - struct mgr_priv_data *mp; - - DSSDBG("writing ovl %d regs extra\n", ovl->id); - - if (!op->extra_info_dirty) - return; - - /* note: write also when op->enabled == false, so that the ovl gets - * disabled */ - - dispc_ovl_enable(ovl->id, op->enabled); - dispc_ovl_set_fifo_threshold(ovl->id, op->fifo_low, op->fifo_high); - - mp = get_mgr_priv(ovl->manager); - - op->extra_info_dirty = false; - if (mp->updating) - op->shadow_extra_info_dirty = true; -} - -static void dss_mgr_write_regs(struct omap_overlay_manager *mgr) -{ - struct mgr_priv_data *mp = get_mgr_priv(mgr); - struct omap_overlay *ovl; - - DSSDBG("writing mgr %d regs\n", mgr->id); - - if (!mp->enabled) - return; - - WARN_ON(mp->busy); - - /* Commit overlay settings */ - list_for_each_entry(ovl, &mgr->overlays, list) { - dss_ovl_write_regs(ovl); - dss_ovl_write_regs_extra(ovl); - } - - if (mp->info_dirty) { - dispc_mgr_setup(mgr->id, &mp->info); - - mp->info_dirty = false; - if (mp->updating) - mp->shadow_info_dirty = true; - } -} - -static void dss_mgr_write_regs_extra(struct omap_overlay_manager *mgr) -{ - struct mgr_priv_data *mp = get_mgr_priv(mgr); - - DSSDBG("writing mgr %d regs extra\n", mgr->id); - - if (!mp->extra_info_dirty) - return; - - dispc_mgr_set_timings(mgr->id, &mp->timings); - - /* lcd_config parameters */ - if (dss_mgr_is_lcd(mgr->id)) - dispc_mgr_set_lcd_config(mgr->id, &mp->lcd_config); - - mp->extra_info_dirty = false; - if (mp->updating) - mp->shadow_extra_info_dirty = true; -} - -static void dss_write_regs(void) -{ - const int num_mgrs = omap_dss_get_num_overlay_managers(); - int i; - - for (i = 0; i < num_mgrs; ++i) { - struct omap_overlay_manager *mgr; - struct mgr_priv_data *mp; - int r; - - mgr = omap_dss_get_overlay_manager(i); - mp = get_mgr_priv(mgr); - - if (!mp->enabled || mgr_manual_update(mgr) || mp->busy) - continue; - - r = dss_check_settings(mgr); - if (r) { - DSSERR("cannot write registers for manager %s: " - "illegal configuration\n", mgr->name); - continue; - } - - dss_mgr_write_regs(mgr); - dss_mgr_write_regs_extra(mgr); - } -} - -static void dss_set_go_bits(void) -{ - const int num_mgrs = omap_dss_get_num_overlay_managers(); - int i; - - for (i = 0; i < num_mgrs; ++i) { - struct omap_overlay_manager *mgr; - struct mgr_priv_data *mp; - - mgr = omap_dss_get_overlay_manager(i); - mp = get_mgr_priv(mgr); - - if (!mp->enabled || mgr_manual_update(mgr) || mp->busy) - continue; - - if (!need_go(mgr)) - continue; - - mp->busy = true; - - if (!dss_data.irq_enabled && need_isr()) - dss_register_vsync_isr(); - - dispc_mgr_go(mgr->id); - } - -} - -static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr) -{ - struct omap_overlay *ovl; - struct mgr_priv_data *mp; - struct ovl_priv_data *op; - - mp = get_mgr_priv(mgr); - mp->shadow_info_dirty = false; - mp->shadow_extra_info_dirty = false; - - list_for_each_entry(ovl, &mgr->overlays, list) { - op = get_ovl_priv(ovl); - op->shadow_info_dirty = false; - op->shadow_extra_info_dirty = false; - } -} - -static int dss_mgr_connect_compat(struct omap_overlay_manager *mgr, - struct omap_dss_device *dst) -{ - return mgr->set_output(mgr, dst); -} - -static void dss_mgr_disconnect_compat(struct omap_overlay_manager *mgr, - struct omap_dss_device *dst) -{ - mgr->unset_output(mgr); -} - -static void dss_mgr_start_update_compat(struct omap_overlay_manager *mgr) -{ - struct mgr_priv_data *mp = get_mgr_priv(mgr); - unsigned long flags; - int r; - - spin_lock_irqsave(&data_lock, flags); - - WARN_ON(mp->updating); - - r = dss_check_settings(mgr); - if (r) { - DSSERR("cannot start manual update: illegal configuration\n"); - spin_unlock_irqrestore(&data_lock, flags); - return; - } - - dss_mgr_write_regs(mgr); - dss_mgr_write_regs_extra(mgr); - - mp->updating = true; - - if (!dss_data.irq_enabled && need_isr()) - dss_register_vsync_isr(); - - dispc_mgr_enable_sync(mgr->id); - - spin_unlock_irqrestore(&data_lock, flags); -} - -static void dss_apply_irq_handler(void *data, u32 mask); - -static void dss_register_vsync_isr(void) -{ - const int num_mgrs = dss_feat_get_num_mgrs(); - u32 mask; - int r, i; - - mask = 0; - for (i = 0; i < num_mgrs; ++i) - mask |= dispc_mgr_get_vsync_irq(i); - - for (i = 0; i < num_mgrs; ++i) - mask |= dispc_mgr_get_framedone_irq(i); - - r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, mask); - WARN_ON(r); - - dss_data.irq_enabled = true; -} - -static void dss_unregister_vsync_isr(void) -{ - const int num_mgrs = dss_feat_get_num_mgrs(); - u32 mask; - int r, i; - - mask = 0; - for (i = 0; i < num_mgrs; ++i) - mask |= dispc_mgr_get_vsync_irq(i); - - for (i = 0; i < num_mgrs; ++i) - mask |= dispc_mgr_get_framedone_irq(i); - - r = omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, mask); - WARN_ON(r); - - dss_data.irq_enabled = false; -} - -static void dss_apply_irq_handler(void *data, u32 mask) -{ - const int num_mgrs = dss_feat_get_num_mgrs(); - int i; - bool extra_updating; - - spin_lock(&data_lock); - - /* clear busy, updating flags, shadow_dirty flags */ - for (i = 0; i < num_mgrs; i++) { - struct omap_overlay_manager *mgr; - struct mgr_priv_data *mp; - - mgr = omap_dss_get_overlay_manager(i); - mp = get_mgr_priv(mgr); - - if (!mp->enabled) - continue; - - mp->updating = dispc_mgr_is_enabled(i); - - if (!mgr_manual_update(mgr)) { - bool was_busy = mp->busy; - mp->busy = dispc_mgr_go_busy(i); - - if (was_busy && !mp->busy) - mgr_clear_shadow_dirty(mgr); - } - } - - dss_write_regs(); - dss_set_go_bits(); - - extra_updating = extra_info_update_ongoing(); - if (!extra_updating) - complete_all(&extra_updated_completion); - - /* call framedone handlers for manual update displays */ - for (i = 0; i < num_mgrs; i++) { - struct omap_overlay_manager *mgr; - struct mgr_priv_data *mp; - - mgr = omap_dss_get_overlay_manager(i); - mp = get_mgr_priv(mgr); - - if (!mgr_manual_update(mgr) || !mp->framedone_handler) - continue; - - if (mask & dispc_mgr_get_framedone_irq(i)) - mp->framedone_handler(mp->framedone_handler_data); - } - - if (!need_isr()) - dss_unregister_vsync_isr(); - - spin_unlock(&data_lock); -} - -static void omap_dss_mgr_apply_ovl(struct omap_overlay *ovl) -{ - struct ovl_priv_data *op; - - op = get_ovl_priv(ovl); - - if (!op->user_info_dirty) - return; - - op->user_info_dirty = false; - op->info_dirty = true; - op->info = op->user_info; -} - -static void omap_dss_mgr_apply_mgr(struct omap_overlay_manager *mgr) -{ - struct mgr_priv_data *mp; - - mp = get_mgr_priv(mgr); - - if (!mp->user_info_dirty) - return; - - mp->user_info_dirty = false; - mp->info_dirty = true; - mp->info = mp->user_info; -} - -static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) -{ - unsigned long flags; - struct omap_overlay *ovl; - int r; - - DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name); - - spin_lock_irqsave(&data_lock, flags); - - r = dss_check_settings_apply(mgr); - if (r) { - spin_unlock_irqrestore(&data_lock, flags); - DSSERR("failed to apply settings: illegal configuration.\n"); - return r; - } - - /* Configure overlays */ - list_for_each_entry(ovl, &mgr->overlays, list) - omap_dss_mgr_apply_ovl(ovl); - - /* Configure manager */ - omap_dss_mgr_apply_mgr(mgr); - - dss_write_regs(); - dss_set_go_bits(); - - spin_unlock_irqrestore(&data_lock, flags); - - return 0; -} - -static void dss_apply_ovl_enable(struct omap_overlay *ovl, bool enable) -{ - struct ovl_priv_data *op; - - op = get_ovl_priv(ovl); - - if (op->enabled == enable) - return; - - op->enabled = enable; - op->extra_info_dirty = true; -} - -static void dss_apply_ovl_fifo_thresholds(struct omap_overlay *ovl, - u32 fifo_low, u32 fifo_high) -{ - struct ovl_priv_data *op = get_ovl_priv(ovl); - - if (op->fifo_low == fifo_low && op->fifo_high == fifo_high) - return; - - op->fifo_low = fifo_low; - op->fifo_high = fifo_high; - op->extra_info_dirty = true; -} - -static void dss_ovl_setup_fifo(struct omap_overlay *ovl) -{ - struct ovl_priv_data *op = get_ovl_priv(ovl); - u32 fifo_low, fifo_high; - bool use_fifo_merge = false; - - if (!op->enabled && !op->enabling) - return; - - dispc_ovl_compute_fifo_thresholds(ovl->id, &fifo_low, &fifo_high, - use_fifo_merge, ovl_manual_update(ovl)); - - dss_apply_ovl_fifo_thresholds(ovl, fifo_low, fifo_high); -} - -static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr) -{ - struct omap_overlay *ovl; - struct mgr_priv_data *mp; - - mp = get_mgr_priv(mgr); - - if (!mp->enabled) - return; - - list_for_each_entry(ovl, &mgr->overlays, list) - dss_ovl_setup_fifo(ovl); -} - -static void dss_setup_fifos(void) -{ - const int num_mgrs = omap_dss_get_num_overlay_managers(); - struct omap_overlay_manager *mgr; - int i; - - for (i = 0; i < num_mgrs; ++i) { - mgr = omap_dss_get_overlay_manager(i); - dss_mgr_setup_fifos(mgr); - } -} - -static int dss_mgr_enable_compat(struct omap_overlay_manager *mgr) -{ - struct mgr_priv_data *mp = get_mgr_priv(mgr); - unsigned long flags; - int r; - - mutex_lock(&apply_lock); - - if (mp->enabled) - goto out; - - spin_lock_irqsave(&data_lock, flags); - - mp->enabled = true; - - r = dss_check_settings(mgr); - if (r) { - DSSERR("failed to enable manager %d: check_settings failed\n", - mgr->id); - goto err; - } - - dss_setup_fifos(); - - dss_write_regs(); - dss_set_go_bits(); - - if (!mgr_manual_update(mgr)) - mp->updating = true; - - if (!dss_data.irq_enabled && need_isr()) - dss_register_vsync_isr(); - - spin_unlock_irqrestore(&data_lock, flags); - - if (!mgr_manual_update(mgr)) - dispc_mgr_enable_sync(mgr->id); - -out: - mutex_unlock(&apply_lock); - - return 0; - -err: - mp->enabled = false; - spin_unlock_irqrestore(&data_lock, flags); - mutex_unlock(&apply_lock); - return r; -} - -static void dss_mgr_disable_compat(struct omap_overlay_manager *mgr) -{ - struct mgr_priv_data *mp = get_mgr_priv(mgr); - unsigned long flags; - - mutex_lock(&apply_lock); - - if (!mp->enabled) - goto out; - - wait_pending_extra_info_updates(); - - if (!mgr_manual_update(mgr)) - dispc_mgr_disable_sync(mgr->id); - - spin_lock_irqsave(&data_lock, flags); - - mp->updating = false; - mp->enabled = false; - - spin_unlock_irqrestore(&data_lock, flags); - -out: - mutex_unlock(&apply_lock); -} - -static int dss_mgr_set_info(struct omap_overlay_manager *mgr, - struct omap_overlay_manager_info *info) -{ - struct mgr_priv_data *mp = get_mgr_priv(mgr); - unsigned long flags; - int r; - - r = dss_mgr_simple_check(mgr, info); - if (r) - return r; - - spin_lock_irqsave(&data_lock, flags); - - mp->user_info = *info; - mp->user_info_dirty = true; - - spin_unlock_irqrestore(&data_lock, flags); - - return 0; -} - -static void dss_mgr_get_info(struct omap_overlay_manager *mgr, - struct omap_overlay_manager_info *info) -{ - struct mgr_priv_data *mp = get_mgr_priv(mgr); - unsigned long flags; - - spin_lock_irqsave(&data_lock, flags); - - *info = mp->user_info; - - spin_unlock_irqrestore(&data_lock, flags); -} - -static int dss_mgr_set_output(struct omap_overlay_manager *mgr, - struct omap_dss_device *output) -{ - int r; - - mutex_lock(&apply_lock); - - if (mgr->output) { - DSSERR("manager %s is already connected to an output\n", - mgr->name); - r = -EINVAL; - goto err; - } - - if ((mgr->supported_outputs & output->id) == 0) { - DSSERR("output does not support manager %s\n", - mgr->name); - r = -EINVAL; - goto err; - } - - output->manager = mgr; - mgr->output = output; - - mutex_unlock(&apply_lock); - - return 0; -err: - mutex_unlock(&apply_lock); - return r; -} - -static int dss_mgr_unset_output(struct omap_overlay_manager *mgr) -{ - int r; - struct mgr_priv_data *mp = get_mgr_priv(mgr); - unsigned long flags; - - mutex_lock(&apply_lock); - - if (!mgr->output) { - DSSERR("failed to unset output, output not set\n"); - r = -EINVAL; - goto err; - } - - spin_lock_irqsave(&data_lock, flags); - - if (mp->enabled) { - DSSERR("output can't be unset when manager is enabled\n"); - r = -EINVAL; - goto err1; - } - - spin_unlock_irqrestore(&data_lock, flags); - - mgr->output->manager = NULL; - mgr->output = NULL; - - mutex_unlock(&apply_lock); - - return 0; -err1: - spin_unlock_irqrestore(&data_lock, flags); -err: - mutex_unlock(&apply_lock); - - return r; -} - -static void dss_apply_mgr_timings(struct omap_overlay_manager *mgr, - const struct omap_video_timings *timings) -{ - struct mgr_priv_data *mp = get_mgr_priv(mgr); - - mp->timings = *timings; - mp->extra_info_dirty = true; -} - -static void dss_mgr_set_timings_compat(struct omap_overlay_manager *mgr, - const struct omap_video_timings *timings) -{ - unsigned long flags; - struct mgr_priv_data *mp = get_mgr_priv(mgr); - - spin_lock_irqsave(&data_lock, flags); - - if (mp->updating) { - DSSERR("cannot set timings for %s: manager needs to be disabled\n", - mgr->name); - goto out; - } - - dss_apply_mgr_timings(mgr, timings); -out: - spin_unlock_irqrestore(&data_lock, flags); -} - -static void dss_apply_mgr_lcd_config(struct omap_overlay_manager *mgr, - const struct dss_lcd_mgr_config *config) -{ - struct mgr_priv_data *mp = get_mgr_priv(mgr); - - mp->lcd_config = *config; - mp->extra_info_dirty = true; -} - -static void dss_mgr_set_lcd_config_compat(struct omap_overlay_manager *mgr, - const struct dss_lcd_mgr_config *config) -{ - unsigned long flags; - struct mgr_priv_data *mp = get_mgr_priv(mgr); - - spin_lock_irqsave(&data_lock, flags); - - if (mp->enabled) { - DSSERR("cannot apply lcd config for %s: manager needs to be disabled\n", - mgr->name); - goto out; - } - - dss_apply_mgr_lcd_config(mgr, config); -out: - spin_unlock_irqrestore(&data_lock, flags); -} - -static int dss_ovl_set_info(struct omap_overlay *ovl, - struct omap_overlay_info *info) -{ - struct ovl_priv_data *op = get_ovl_priv(ovl); - unsigned long flags; - int r; - - r = dss_ovl_simple_check(ovl, info); - if (r) - return r; - - spin_lock_irqsave(&data_lock, flags); - - op->user_info = *info; - op->user_info_dirty = true; - - spin_unlock_irqrestore(&data_lock, flags); - - return 0; -} - -static void dss_ovl_get_info(struct omap_overlay *ovl, - struct omap_overlay_info *info) -{ - struct ovl_priv_data *op = get_ovl_priv(ovl); - unsigned long flags; - - spin_lock_irqsave(&data_lock, flags); - - *info = op->user_info; - - spin_unlock_irqrestore(&data_lock, flags); -} - -static int dss_ovl_set_manager(struct omap_overlay *ovl, - struct omap_overlay_manager *mgr) -{ - struct ovl_priv_data *op = get_ovl_priv(ovl); - unsigned long flags; - int r; - - if (!mgr) - return -EINVAL; - - mutex_lock(&apply_lock); - - if (ovl->manager) { - DSSERR("overlay '%s' already has a manager '%s'\n", - ovl->name, ovl->manager->name); - r = -EINVAL; - goto err; - } - - r = dispc_runtime_get(); - if (r) - goto err; - - spin_lock_irqsave(&data_lock, flags); - - if (op->enabled) { - spin_unlock_irqrestore(&data_lock, flags); - DSSERR("overlay has to be disabled to change the manager\n"); - r = -EINVAL; - goto err1; - } - - dispc_ovl_set_channel_out(ovl->id, mgr->id); - - ovl->manager = mgr; - list_add_tail(&ovl->list, &mgr->overlays); - - spin_unlock_irqrestore(&data_lock, flags); - - dispc_runtime_put(); - - mutex_unlock(&apply_lock); - - return 0; - -err1: - dispc_runtime_put(); -err: - mutex_unlock(&apply_lock); - return r; -} - -static int dss_ovl_unset_manager(struct omap_overlay *ovl) -{ - struct ovl_priv_data *op = get_ovl_priv(ovl); - unsigned long flags; - int r; - - mutex_lock(&apply_lock); - - if (!ovl->manager) { - DSSERR("failed to detach overlay: manager not set\n"); - r = -EINVAL; - goto err; - } - - spin_lock_irqsave(&data_lock, flags); - - if (op->enabled) { - spin_unlock_irqrestore(&data_lock, flags); - DSSERR("overlay has to be disabled to unset the manager\n"); - r = -EINVAL; - goto err; - } - - spin_unlock_irqrestore(&data_lock, flags); - - /* wait for pending extra_info updates to ensure the ovl is disabled */ - wait_pending_extra_info_updates(); - - /* - * For a manual update display, there is no guarantee that the overlay - * is really disabled in HW, we may need an extra update from this - * manager before the configurations can go in. Return an error if the - * overlay needed an update from the manager. - * - * TODO: Instead of returning an error, try to do a dummy manager update - * here to disable the overlay in hardware. Use the *GATED fields in - * the DISPC_CONFIG registers to do a dummy update. - */ - spin_lock_irqsave(&data_lock, flags); - - if (ovl_manual_update(ovl) && op->extra_info_dirty) { - spin_unlock_irqrestore(&data_lock, flags); - DSSERR("need an update to change the manager\n"); - r = -EINVAL; - goto err; - } - - ovl->manager = NULL; - list_del(&ovl->list); - - spin_unlock_irqrestore(&data_lock, flags); - - mutex_unlock(&apply_lock); - - return 0; -err: - mutex_unlock(&apply_lock); - return r; -} - -static bool dss_ovl_is_enabled(struct omap_overlay *ovl) -{ - struct ovl_priv_data *op = get_ovl_priv(ovl); - unsigned long flags; - bool e; - - spin_lock_irqsave(&data_lock, flags); - - e = op->enabled; - - spin_unlock_irqrestore(&data_lock, flags); - - return e; -} - -static int dss_ovl_enable(struct omap_overlay *ovl) -{ - struct ovl_priv_data *op = get_ovl_priv(ovl); - unsigned long flags; - int r; - - mutex_lock(&apply_lock); - - if (op->enabled) { - r = 0; - goto err1; - } - - if (ovl->manager == NULL || ovl->manager->output == NULL) { - r = -EINVAL; - goto err1; - } - - spin_lock_irqsave(&data_lock, flags); - - op->enabling = true; - - r = dss_check_settings(ovl->manager); - if (r) { - DSSERR("failed to enable overlay %d: check_settings failed\n", - ovl->id); - goto err2; - } - - dss_setup_fifos(); - - op->enabling = false; - dss_apply_ovl_enable(ovl, true); - - dss_write_regs(); - dss_set_go_bits(); - - spin_unlock_irqrestore(&data_lock, flags); - - mutex_unlock(&apply_lock); - - return 0; -err2: - op->enabling = false; - spin_unlock_irqrestore(&data_lock, flags); -err1: - mutex_unlock(&apply_lock); - return r; -} - -static int dss_ovl_disable(struct omap_overlay *ovl) -{ - struct ovl_priv_data *op = get_ovl_priv(ovl); - unsigned long flags; - int r; - - mutex_lock(&apply_lock); - - if (!op->enabled) { - r = 0; - goto err; - } - - if (ovl->manager == NULL || ovl->manager->output == NULL) { - r = -EINVAL; - goto err; - } - - spin_lock_irqsave(&data_lock, flags); - - dss_apply_ovl_enable(ovl, false); - dss_write_regs(); - dss_set_go_bits(); - - spin_unlock_irqrestore(&data_lock, flags); - - mutex_unlock(&apply_lock); - - return 0; - -err: - mutex_unlock(&apply_lock); - return r; -} - -static int dss_mgr_register_framedone_handler_compat(struct omap_overlay_manager *mgr, - void (*handler)(void *), void *data) -{ - struct mgr_priv_data *mp = get_mgr_priv(mgr); - - if (mp->framedone_handler) - return -EBUSY; - - mp->framedone_handler = handler; - mp->framedone_handler_data = data; - - return 0; -} - -static void dss_mgr_unregister_framedone_handler_compat(struct omap_overlay_manager *mgr, - void (*handler)(void *), void *data) -{ - struct mgr_priv_data *mp = get_mgr_priv(mgr); - - WARN_ON(mp->framedone_handler != handler || - mp->framedone_handler_data != data); - - mp->framedone_handler = NULL; - mp->framedone_handler_data = NULL; -} - -static const struct dss_mgr_ops apply_mgr_ops = { - .connect = dss_mgr_connect_compat, - .disconnect = dss_mgr_disconnect_compat, - .start_update = dss_mgr_start_update_compat, - .enable = dss_mgr_enable_compat, - .disable = dss_mgr_disable_compat, - .set_timings = dss_mgr_set_timings_compat, - .set_lcd_config = dss_mgr_set_lcd_config_compat, - .register_framedone_handler = dss_mgr_register_framedone_handler_compat, - .unregister_framedone_handler = dss_mgr_unregister_framedone_handler_compat, -}; - -static int compat_refcnt; -static DEFINE_MUTEX(compat_init_lock); - -int omapdss_compat_init(void) -{ - struct platform_device *pdev = dss_get_core_pdev(); - int i, r; - - mutex_lock(&compat_init_lock); - - if (compat_refcnt++ > 0) - goto out; - - apply_init_priv(); - - dss_init_overlay_managers_sysfs(pdev); - dss_init_overlays(pdev); - - for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) { - struct omap_overlay_manager *mgr; - - mgr = omap_dss_get_overlay_manager(i); - - mgr->set_output = &dss_mgr_set_output; - mgr->unset_output = &dss_mgr_unset_output; - mgr->apply = &omap_dss_mgr_apply; - mgr->set_manager_info = &dss_mgr_set_info; - mgr->get_manager_info = &dss_mgr_get_info; - mgr->wait_for_go = &dss_mgr_wait_for_go; - mgr->wait_for_vsync = &dss_mgr_wait_for_vsync; - mgr->get_device = &dss_mgr_get_device; - } - - for (i = 0; i < omap_dss_get_num_overlays(); i++) { - struct omap_overlay *ovl = omap_dss_get_overlay(i); - - ovl->is_enabled = &dss_ovl_is_enabled; - ovl->enable = &dss_ovl_enable; - ovl->disable = &dss_ovl_disable; - ovl->set_manager = &dss_ovl_set_manager; - ovl->unset_manager = &dss_ovl_unset_manager; - ovl->set_overlay_info = &dss_ovl_set_info; - ovl->get_overlay_info = &dss_ovl_get_info; - ovl->wait_for_go = &dss_mgr_wait_for_go_ovl; - ovl->get_device = &dss_ovl_get_device; - } - - r = dss_install_mgr_ops(&apply_mgr_ops); - if (r) - goto err_mgr_ops; - - r = display_init_sysfs(pdev); - if (r) - goto err_disp_sysfs; - - dispc_runtime_get(); - - r = dss_dispc_initialize_irq(); - if (r) - goto err_init_irq; - - dispc_runtime_put(); - -out: - mutex_unlock(&compat_init_lock); - - return 0; - -err_init_irq: - dispc_runtime_put(); - display_uninit_sysfs(pdev); - -err_disp_sysfs: - dss_uninstall_mgr_ops(); - -err_mgr_ops: - dss_uninit_overlay_managers_sysfs(pdev); - dss_uninit_overlays(pdev); - - compat_refcnt--; - - mutex_unlock(&compat_init_lock); - - return r; -} -EXPORT_SYMBOL(omapdss_compat_init); - -void omapdss_compat_uninit(void) -{ - struct platform_device *pdev = dss_get_core_pdev(); - - mutex_lock(&compat_init_lock); - - if (--compat_refcnt > 0) - goto out; - - dss_dispc_uninitialize_irq(); - - display_uninit_sysfs(pdev); - - dss_uninstall_mgr_ops(); - - dss_uninit_overlay_managers_sysfs(pdev); - dss_uninit_overlays(pdev); -out: - mutex_unlock(&compat_init_lock); -} -EXPORT_SYMBOL(omapdss_compat_uninit); diff --git a/drivers/gpu/drm/omapdrm/dss/core.c b/drivers/gpu/drm/omapdrm/dss/core.c index 54eeb507f9b3..7e4e5bebabbe 100644 --- a/drivers/gpu/drm/omapdrm/dss/core.c +++ b/drivers/gpu/drm/omapdrm/dss/core.c @@ -165,32 +165,20 @@ int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)) #endif /* CONFIG_OMAP2_DSS_DEBUGFS */ /* PLATFORM DEVICE */ -static int omap_dss_pm_notif(struct notifier_block *b, unsigned long v, void *d) + +static void dss_disable_all_devices(void) { - DSSDBG("pm notif %lu\n", v); - - switch (v) { - case PM_SUSPEND_PREPARE: - case PM_HIBERNATION_PREPARE: - case PM_RESTORE_PREPARE: - DSSDBG("suspending displays\n"); - return dss_suspend_all_devices(); - - case PM_POST_SUSPEND: - case PM_POST_HIBERNATION: - case PM_POST_RESTORE: - DSSDBG("resuming displays\n"); - return dss_resume_all_devices(); - - default: - return 0; + struct omap_dss_device *dssdev = NULL; + + for_each_dss_dev(dssdev) { + if (!dssdev->driver) + continue; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) + dssdev->driver->disable(dssdev); } } -static struct notifier_block omap_dss_pm_notif_block = { - .notifier_call = omap_dss_pm_notif, -}; - static int __init omap_dss_probe(struct platform_device *pdev) { struct omap_dss_board_info *pdata = pdev->dev.platform_data; @@ -211,8 +199,6 @@ static int __init omap_dss_probe(struct platform_device *pdev) else if (pdata->default_device) core.default_display_name = pdata->default_device->name; - register_pm_notifier(&omap_dss_pm_notif_block); - return 0; err_debugfs: @@ -222,8 +208,6 @@ err_debugfs: static int omap_dss_remove(struct platform_device *pdev) { - unregister_pm_notifier(&omap_dss_pm_notif_block); - dss_uninitialize_debugfs(); return 0; diff --git a/drivers/gpu/drm/omapdrm/dss/dispc-compat.c b/drivers/gpu/drm/omapdrm/dss/dispc-compat.c deleted file mode 100644 index 0918b3bfe82a..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/dispc-compat.c +++ /dev/null @@ -1,667 +0,0 @@ -/* - * Copyright (C) 2012 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#define DSS_SUBSYS_NAME "APPLY" - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/jiffies.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/seq_file.h> - -#include <video/omapdss.h> - -#include "dss.h" -#include "dss_features.h" -#include "dispc-compat.h" - -#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ - DISPC_IRQ_OCP_ERR | \ - DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ - DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ - DISPC_IRQ_SYNC_LOST | \ - DISPC_IRQ_SYNC_LOST_DIGIT) - -#define DISPC_MAX_NR_ISRS 8 - -struct omap_dispc_isr_data { - omap_dispc_isr_t isr; - void *arg; - u32 mask; -}; - -struct dispc_irq_stats { - unsigned long last_reset; - unsigned irq_count; - unsigned irqs[32]; -}; - -static struct { - spinlock_t irq_lock; - u32 irq_error_mask; - struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; - u32 error_irqs; - struct work_struct error_work; - -#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - spinlock_t irq_stats_lock; - struct dispc_irq_stats irq_stats; -#endif -} dispc_compat; - - -#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS -static void dispc_dump_irqs(struct seq_file *s) -{ - unsigned long flags; - struct dispc_irq_stats stats; - - spin_lock_irqsave(&dispc_compat.irq_stats_lock, flags); - - stats = dispc_compat.irq_stats; - memset(&dispc_compat.irq_stats, 0, sizeof(dispc_compat.irq_stats)); - dispc_compat.irq_stats.last_reset = jiffies; - - spin_unlock_irqrestore(&dispc_compat.irq_stats_lock, flags); - - seq_printf(s, "period %u ms\n", - jiffies_to_msecs(jiffies - stats.last_reset)); - - seq_printf(s, "irqs %d\n", stats.irq_count); -#define PIS(x) \ - seq_printf(s, "%-20s %10d\n", #x, stats.irqs[ffs(DISPC_IRQ_##x)-1]); - - PIS(FRAMEDONE); - PIS(VSYNC); - PIS(EVSYNC_EVEN); - PIS(EVSYNC_ODD); - PIS(ACBIAS_COUNT_STAT); - PIS(PROG_LINE_NUM); - PIS(GFX_FIFO_UNDERFLOW); - PIS(GFX_END_WIN); - PIS(PAL_GAMMA_MASK); - PIS(OCP_ERR); - PIS(VID1_FIFO_UNDERFLOW); - PIS(VID1_END_WIN); - PIS(VID2_FIFO_UNDERFLOW); - PIS(VID2_END_WIN); - if (dss_feat_get_num_ovls() > 3) { - PIS(VID3_FIFO_UNDERFLOW); - PIS(VID3_END_WIN); - } - PIS(SYNC_LOST); - PIS(SYNC_LOST_DIGIT); - PIS(WAKEUP); - if (dss_has_feature(FEAT_MGR_LCD2)) { - PIS(FRAMEDONE2); - PIS(VSYNC2); - PIS(ACBIAS_COUNT_STAT2); - PIS(SYNC_LOST2); - } - if (dss_has_feature(FEAT_MGR_LCD3)) { - PIS(FRAMEDONE3); - PIS(VSYNC3); - PIS(ACBIAS_COUNT_STAT3); - PIS(SYNC_LOST3); - } -#undef PIS -} -#endif - -/* dispc.irq_lock has to be locked by the caller */ -static void _omap_dispc_set_irqs(void) -{ - u32 mask; - int i; - struct omap_dispc_isr_data *isr_data; - - mask = dispc_compat.irq_error_mask; - - for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { - isr_data = &dispc_compat.registered_isr[i]; - - if (isr_data->isr == NULL) - continue; - - mask |= isr_data->mask; - } - - dispc_write_irqenable(mask); -} - -int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask) -{ - int i; - int ret; - unsigned long flags; - struct omap_dispc_isr_data *isr_data; - - if (isr == NULL) - return -EINVAL; - - spin_lock_irqsave(&dispc_compat.irq_lock, flags); - - /* check for duplicate entry */ - for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { - isr_data = &dispc_compat.registered_isr[i]; - if (isr_data->isr == isr && isr_data->arg == arg && - isr_data->mask == mask) { - ret = -EINVAL; - goto err; - } - } - - isr_data = NULL; - ret = -EBUSY; - - for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { - isr_data = &dispc_compat.registered_isr[i]; - - if (isr_data->isr != NULL) - continue; - - isr_data->isr = isr; - isr_data->arg = arg; - isr_data->mask = mask; - ret = 0; - - break; - } - - if (ret) - goto err; - - _omap_dispc_set_irqs(); - - spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); - - return 0; -err: - spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); - - return ret; -} -EXPORT_SYMBOL(omap_dispc_register_isr); - -int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask) -{ - int i; - unsigned long flags; - int ret = -EINVAL; - struct omap_dispc_isr_data *isr_data; - - spin_lock_irqsave(&dispc_compat.irq_lock, flags); - - for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { - isr_data = &dispc_compat.registered_isr[i]; - if (isr_data->isr != isr || isr_data->arg != arg || - isr_data->mask != mask) - continue; - - /* found the correct isr */ - - isr_data->isr = NULL; - isr_data->arg = NULL; - isr_data->mask = 0; - - ret = 0; - break; - } - - if (ret == 0) - _omap_dispc_set_irqs(); - - spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); - - return ret; -} -EXPORT_SYMBOL(omap_dispc_unregister_isr); - -static void print_irq_status(u32 status) -{ - if ((status & dispc_compat.irq_error_mask) == 0) - return; - -#define PIS(x) (status & DISPC_IRQ_##x) ? (#x " ") : "" - - pr_debug("DISPC IRQ: 0x%x: %s%s%s%s%s%s%s%s%s\n", - status, - PIS(OCP_ERR), - PIS(GFX_FIFO_UNDERFLOW), - PIS(VID1_FIFO_UNDERFLOW), - PIS(VID2_FIFO_UNDERFLOW), - dss_feat_get_num_ovls() > 3 ? PIS(VID3_FIFO_UNDERFLOW) : "", - PIS(SYNC_LOST), - PIS(SYNC_LOST_DIGIT), - dss_has_feature(FEAT_MGR_LCD2) ? PIS(SYNC_LOST2) : "", - dss_has_feature(FEAT_MGR_LCD3) ? PIS(SYNC_LOST3) : ""); -#undef PIS -} - -/* Called from dss.c. Note that we don't touch clocks here, - * but we presume they are on because we got an IRQ. However, - * an irq handler may turn the clocks off, so we may not have - * clock later in the function. */ -static irqreturn_t omap_dispc_irq_handler(int irq, void *arg) -{ - int i; - u32 irqstatus, irqenable; - u32 handledirqs = 0; - u32 unhandled_errors; - struct omap_dispc_isr_data *isr_data; - struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; - - spin_lock(&dispc_compat.irq_lock); - - irqstatus = dispc_read_irqstatus(); - irqenable = dispc_read_irqenable(); - - /* IRQ is not for us */ - if (!(irqstatus & irqenable)) { - spin_unlock(&dispc_compat.irq_lock); - return IRQ_NONE; - } - -#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - spin_lock(&dispc_compat.irq_stats_lock); - dispc_compat.irq_stats.irq_count++; - dss_collect_irq_stats(irqstatus, dispc_compat.irq_stats.irqs); - spin_unlock(&dispc_compat.irq_stats_lock); -#endif - - print_irq_status(irqstatus); - - /* Ack the interrupt. Do it here before clocks are possibly turned - * off */ - dispc_clear_irqstatus(irqstatus); - /* flush posted write */ - dispc_read_irqstatus(); - - /* make a copy and unlock, so that isrs can unregister - * themselves */ - memcpy(registered_isr, dispc_compat.registered_isr, - sizeof(registered_isr)); - - spin_unlock(&dispc_compat.irq_lock); - - for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { - isr_data = ®istered_isr[i]; - - if (!isr_data->isr) - continue; - - if (isr_data->mask & irqstatus) { - isr_data->isr(isr_data->arg, irqstatus); - handledirqs |= isr_data->mask; - } - } - - spin_lock(&dispc_compat.irq_lock); - - unhandled_errors = irqstatus & ~handledirqs & dispc_compat.irq_error_mask; - - if (unhandled_errors) { - dispc_compat.error_irqs |= unhandled_errors; - - dispc_compat.irq_error_mask &= ~unhandled_errors; - _omap_dispc_set_irqs(); - - schedule_work(&dispc_compat.error_work); - } - - spin_unlock(&dispc_compat.irq_lock); - - return IRQ_HANDLED; -} - -static void dispc_error_worker(struct work_struct *work) -{ - int i; - u32 errors; - unsigned long flags; - static const unsigned fifo_underflow_bits[] = { - DISPC_IRQ_GFX_FIFO_UNDERFLOW, - DISPC_IRQ_VID1_FIFO_UNDERFLOW, - DISPC_IRQ_VID2_FIFO_UNDERFLOW, - DISPC_IRQ_VID3_FIFO_UNDERFLOW, - }; - - spin_lock_irqsave(&dispc_compat.irq_lock, flags); - errors = dispc_compat.error_irqs; - dispc_compat.error_irqs = 0; - spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); - - dispc_runtime_get(); - - for (i = 0; i < omap_dss_get_num_overlays(); ++i) { - struct omap_overlay *ovl; - unsigned bit; - - ovl = omap_dss_get_overlay(i); - bit = fifo_underflow_bits[i]; - - if (bit & errors) { - DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n", - ovl->name); - ovl->disable(ovl); - msleep(50); - } - } - - for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { - struct omap_overlay_manager *mgr; - unsigned bit; - - mgr = omap_dss_get_overlay_manager(i); - bit = dispc_mgr_get_sync_lost_irq(i); - - if (bit & errors) { - int j; - - DSSERR("SYNC_LOST on channel %s, restarting the output " - "with video overlays disabled\n", - mgr->name); - - dss_mgr_disable(mgr); - - for (j = 0; j < omap_dss_get_num_overlays(); ++j) { - struct omap_overlay *ovl; - ovl = omap_dss_get_overlay(j); - - if (ovl->id != OMAP_DSS_GFX && - ovl->manager == mgr) - ovl->disable(ovl); - } - - dss_mgr_enable(mgr); - } - } - - if (errors & DISPC_IRQ_OCP_ERR) { - DSSERR("OCP_ERR\n"); - for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { - struct omap_overlay_manager *mgr; - - mgr = omap_dss_get_overlay_manager(i); - dss_mgr_disable(mgr); - } - } - - spin_lock_irqsave(&dispc_compat.irq_lock, flags); - dispc_compat.irq_error_mask |= errors; - _omap_dispc_set_irqs(); - spin_unlock_irqrestore(&dispc_compat.irq_lock, flags); - - dispc_runtime_put(); -} - -int dss_dispc_initialize_irq(void) -{ - int r; - -#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS - spin_lock_init(&dispc_compat.irq_stats_lock); - dispc_compat.irq_stats.last_reset = jiffies; - dss_debugfs_create_file("dispc_irq", dispc_dump_irqs); -#endif - - spin_lock_init(&dispc_compat.irq_lock); - - memset(dispc_compat.registered_isr, 0, - sizeof(dispc_compat.registered_isr)); - - dispc_compat.irq_error_mask = DISPC_IRQ_MASK_ERROR; - if (dss_has_feature(FEAT_MGR_LCD2)) - dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST2; - if (dss_has_feature(FEAT_MGR_LCD3)) - dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST3; - if (dss_feat_get_num_ovls() > 3) - dispc_compat.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW; - - /* - * there's SYNC_LOST_DIGIT waiting after enabling the DSS, - * so clear it - */ - dispc_clear_irqstatus(dispc_read_irqstatus()); - - INIT_WORK(&dispc_compat.error_work, dispc_error_worker); - - _omap_dispc_set_irqs(); - - r = dispc_request_irq(omap_dispc_irq_handler, &dispc_compat); - if (r) { - DSSERR("dispc_request_irq failed\n"); - return r; - } - - return 0; -} - -void dss_dispc_uninitialize_irq(void) -{ - dispc_free_irq(&dispc_compat); -} - -static void dispc_mgr_disable_isr(void *data, u32 mask) -{ - struct completion *compl = data; - complete(compl); -} - -static void dispc_mgr_enable_lcd_out(enum omap_channel channel) -{ - dispc_mgr_enable(channel, true); -} - -static void dispc_mgr_disable_lcd_out(enum omap_channel channel) -{ - DECLARE_COMPLETION_ONSTACK(framedone_compl); - int r; - u32 irq; - - if (!dispc_mgr_is_enabled(channel)) - return; - - /* - * When we disable LCD output, we need to wait for FRAMEDONE to know - * that DISPC has finished with the LCD output. - */ - - irq = dispc_mgr_get_framedone_irq(channel); - - r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl, - irq); - if (r) - DSSERR("failed to register FRAMEDONE isr\n"); - - dispc_mgr_enable(channel, false); - - /* if we couldn't register for framedone, just sleep and exit */ - if (r) { - msleep(100); - return; - } - - if (!wait_for_completion_timeout(&framedone_compl, - msecs_to_jiffies(100))) - DSSERR("timeout waiting for FRAME DONE\n"); - - r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl, - irq); - if (r) - DSSERR("failed to unregister FRAMEDONE isr\n"); -} - -static void dispc_digit_out_enable_isr(void *data, u32 mask) -{ - struct completion *compl = data; - - /* ignore any sync lost interrupts */ - if (mask & (DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD)) - complete(compl); -} - -static void dispc_mgr_enable_digit_out(void) -{ - DECLARE_COMPLETION_ONSTACK(vsync_compl); - int r; - u32 irq_mask; - - if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT)) - return; - - /* - * Digit output produces some sync lost interrupts during the first - * frame when enabling. Those need to be ignored, so we register for the - * sync lost irq to prevent the error handler from triggering. - */ - - irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT) | - dispc_mgr_get_sync_lost_irq(OMAP_DSS_CHANNEL_DIGIT); - - r = omap_dispc_register_isr(dispc_digit_out_enable_isr, &vsync_compl, - irq_mask); - if (r) { - DSSERR("failed to register %x isr\n", irq_mask); - return; - } - - dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, true); - - /* wait for the first evsync */ - if (!wait_for_completion_timeout(&vsync_compl, msecs_to_jiffies(100))) - DSSERR("timeout waiting for digit out to start\n"); - - r = omap_dispc_unregister_isr(dispc_digit_out_enable_isr, &vsync_compl, - irq_mask); - if (r) - DSSERR("failed to unregister %x isr\n", irq_mask); -} - -static void dispc_mgr_disable_digit_out(void) -{ - DECLARE_COMPLETION_ONSTACK(framedone_compl); - int r, i; - u32 irq_mask; - int num_irqs; - - if (!dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT)) - return; - - /* - * When we disable the digit output, we need to wait for FRAMEDONE to - * know that DISPC has finished with the output. - */ - - irq_mask = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_DIGIT); - num_irqs = 1; - - if (!irq_mask) { - /* - * omap 2/3 don't have framedone irq for TV, so we need to use - * vsyncs for this. - */ - - irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT); - /* - * We need to wait for both even and odd vsyncs. Note that this - * is not totally reliable, as we could get a vsync interrupt - * before we disable the output, which leads to timeout in the - * wait_for_completion. - */ - num_irqs = 2; - } - - r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl, - irq_mask); - if (r) - DSSERR("failed to register %x isr\n", irq_mask); - - dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, false); - - /* if we couldn't register the irq, just sleep and exit */ - if (r) { - msleep(100); - return; - } - - for (i = 0; i < num_irqs; ++i) { - if (!wait_for_completion_timeout(&framedone_compl, - msecs_to_jiffies(100))) - DSSERR("timeout waiting for digit out to stop\n"); - } - - r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl, - irq_mask); - if (r) - DSSERR("failed to unregister %x isr\n", irq_mask); -} - -void dispc_mgr_enable_sync(enum omap_channel channel) -{ - if (dss_mgr_is_lcd(channel)) - dispc_mgr_enable_lcd_out(channel); - else if (channel == OMAP_DSS_CHANNEL_DIGIT) - dispc_mgr_enable_digit_out(); - else - WARN_ON(1); -} - -void dispc_mgr_disable_sync(enum omap_channel channel) -{ - if (dss_mgr_is_lcd(channel)) - dispc_mgr_disable_lcd_out(channel); - else if (channel == OMAP_DSS_CHANNEL_DIGIT) - dispc_mgr_disable_digit_out(); - else - WARN_ON(1); -} - -static inline void dispc_irq_wait_handler(void *data, u32 mask) -{ - complete((struct completion *)data); -} - -int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, - unsigned long timeout) -{ - - int r; - DECLARE_COMPLETION_ONSTACK(completion); - - r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion, - irqmask); - - if (r) - return r; - - timeout = wait_for_completion_interruptible_timeout(&completion, - timeout); - - omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask); - - if (timeout == 0) - return -ETIMEDOUT; - - if (timeout == -ERESTARTSYS) - return -ERESTARTSYS; - - return 0; -} diff --git a/drivers/gpu/drm/omapdrm/dss/dispc-compat.h b/drivers/gpu/drm/omapdrm/dss/dispc-compat.h deleted file mode 100644 index 14a69b3d4fb0..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/dispc-compat.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2012 Texas Instruments - * Author: Tomi Valkeinen <tomi.valkeinen@ti.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. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef __OMAP2_DSS_DISPC_COMPAT_H -#define __OMAP2_DSS_DISPC_COMPAT_H - -void dispc_mgr_enable_sync(enum omap_channel channel); -void dispc_mgr_disable_sync(enum omap_channel channel); - -int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, - unsigned long timeout); - -int dss_dispc_initialize_irq(void); -void dss_dispc_uninitialize_irq(void); - -#endif diff --git a/drivers/gpu/drm/omapdrm/dss/dispc.c b/drivers/gpu/drm/omapdrm/dss/dispc.c index 6b50476ec669..f83608b69e68 100644 --- a/drivers/gpu/drm/omapdrm/dss/dispc.c +++ b/drivers/gpu/drm/omapdrm/dss/dispc.c @@ -104,6 +104,15 @@ struct dispc_features { bool supports_sync_align:1; bool has_writeback:1; + + bool supports_double_pixel:1; + + /* + * Field order for VENC is different than HDMI. We should handle this in + * some intelligent manner, but as the SoCs have either HDMI or VENC, + * never both, we can just use this flag for now. + */ + bool reverse_ilace_field_order:1; }; #define DISPC_MAX_NR_FIFOS 5 @@ -2552,47 +2561,6 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk, return 0; } -int dispc_ovl_check(enum omap_plane plane, enum omap_channel channel, - const struct omap_overlay_info *oi, - const struct omap_video_timings *timings, - int *x_predecim, int *y_predecim) -{ - enum omap_overlay_caps caps = dss_feat_get_overlay_caps(plane); - bool five_taps = true; - bool fieldmode = false; - u16 in_height = oi->height; - u16 in_width = oi->width; - bool ilace = timings->interlace; - u16 out_width, out_height; - int pos_x = oi->pos_x; - unsigned long pclk = dispc_mgr_pclk_rate(channel); - unsigned long lclk = dispc_mgr_lclk_rate(channel); - - out_width = oi->out_width == 0 ? oi->width : oi->out_width; - out_height = oi->out_height == 0 ? oi->height : oi->out_height; - - if (ilace && oi->height == out_height) - fieldmode = true; - - if (ilace) { - if (fieldmode) - in_height /= 2; - out_height /= 2; - - DSSDBG("adjusting for ilace: height %d, out_height %d\n", - in_height, out_height); - } - - if (!dss_feat_color_mode_supported(plane, oi->color_mode)) - return -EINVAL; - - return dispc_ovl_calc_scaling(pclk, lclk, caps, timings, in_width, - in_height, out_width, out_height, oi->color_mode, - &five_taps, x_predecim, y_predecim, pos_x, - oi->rotation_type, false); -} -EXPORT_SYMBOL(dispc_ovl_check); - static int dispc_ovl_setup_common(enum omap_plane plane, enum omap_overlay_caps caps, u32 paddr, u32 p_uv_addr, u16 screen_width, int pos_x, int pos_y, u16 width, u16 height, @@ -2747,6 +2715,9 @@ static int dispc_ovl_setup_common(enum omap_plane plane, dispc_ovl_configure_burst_type(plane, rotation_type); + if (dispc.feat->reverse_ilace_field_order) + swap(offset0, offset1); + dispc_ovl_set_ba0(plane, paddr + offset0); dispc_ovl_set_ba1(plane, paddr + offset1); @@ -2898,6 +2869,12 @@ bool dispc_ovl_enabled(enum omap_plane plane) } EXPORT_SYMBOL(dispc_ovl_enabled); +enum omap_dss_output_id dispc_mgr_get_supported_outputs(enum omap_channel channel) +{ + return dss_feat_get_supported_outputs(channel); +} +EXPORT_SYMBOL(dispc_mgr_get_supported_outputs); + void dispc_mgr_enable(enum omap_channel channel, bool enable) { mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable); @@ -3287,6 +3264,10 @@ void dispc_mgr_set_timings(enum omap_channel channel, } else { if (t.interlace) t.y_res /= 2; + + if (dispc.feat->supports_double_pixel) + REG_FLD_MOD(DISPC_CONTROL, t.double_pixel ? 1 : 0, + 19, 17); } dispc_mgr_set_size(channel, t.x_res, t.y_res); @@ -3951,6 +3932,8 @@ static const struct dispc_features omap44xx_dispc_feats = { .set_max_preload = true, .supports_sync_align = true, .has_writeback = true, + .supports_double_pixel = true, + .reverse_ilace_field_order = true, }; static const struct dispc_features omap54xx_dispc_feats = { @@ -3974,6 +3957,8 @@ static const struct dispc_features omap54xx_dispc_feats = { .set_max_preload = true, .supports_sync_align = true, .has_writeback = true, + .supports_double_pixel = true, + .reverse_ilace_field_order = true, }; static int dispc_init_features(struct platform_device *pdev) @@ -4129,8 +4114,6 @@ static int dispc_bind(struct device *dev, struct device *master, void *data) dispc_runtime_put(); - dss_init_overlay_managers(); - dss_debugfs_create_file("dispc", dispc_dump_regs); return 0; @@ -4144,8 +4127,6 @@ static void dispc_unbind(struct device *dev, struct device *master, void *data) { pm_runtime_disable(dev); - - dss_uninit_overlay_managers(); } static const struct component_ops dispc_component_ops = { diff --git a/drivers/gpu/drm/omapdrm/dss/display-sysfs.c b/drivers/gpu/drm/omapdrm/dss/display-sysfs.c deleted file mode 100644 index 6ad0991f8259..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/display-sysfs.c +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> - * - * Some code and ideas taken from drivers/video/omap/ driver - * by Imre Deak. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#define DSS_SUBSYS_NAME "DISPLAY" - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/sysfs.h> - -#include <video/omapdss.h> -#include "dss.h" - -static ssize_t display_name_show(struct omap_dss_device *dssdev, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%s\n", - dssdev->name ? - dssdev->name : ""); -} - -static ssize_t display_enabled_show(struct omap_dss_device *dssdev, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", - omapdss_device_is_enabled(dssdev)); -} - -static ssize_t display_enabled_store(struct omap_dss_device *dssdev, - const char *buf, size_t size) -{ - int r; - bool enable; - - r = strtobool(buf, &enable); - if (r) - return r; - - if (enable == omapdss_device_is_enabled(dssdev)) - return size; - - if (omapdss_device_is_connected(dssdev) == false) - return -ENODEV; - - if (enable) { - r = dssdev->driver->enable(dssdev); - if (r) - return r; - } else { - dssdev->driver->disable(dssdev); - } - - return size; -} - -static ssize_t display_tear_show(struct omap_dss_device *dssdev, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", - dssdev->driver->get_te ? - dssdev->driver->get_te(dssdev) : 0); -} - -static ssize_t display_tear_store(struct omap_dss_device *dssdev, - const char *buf, size_t size) -{ - int r; - bool te; - - if (!dssdev->driver->enable_te || !dssdev->driver->get_te) - return -ENOENT; - - r = strtobool(buf, &te); - if (r) - return r; - - r = dssdev->driver->enable_te(dssdev, te); - if (r) - return r; - - return size; -} - -static ssize_t display_timings_show(struct omap_dss_device *dssdev, char *buf) -{ - struct omap_video_timings t; - - if (!dssdev->driver->get_timings) - return -ENOENT; - - dssdev->driver->get_timings(dssdev, &t); - - return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n", - t.pixelclock, - t.x_res, t.hfp, t.hbp, t.hsw, - t.y_res, t.vfp, t.vbp, t.vsw); -} - -static ssize_t display_timings_store(struct omap_dss_device *dssdev, - const char *buf, size_t size) -{ - struct omap_video_timings t = dssdev->panel.timings; - int r, found; - - if (!dssdev->driver->set_timings || !dssdev->driver->check_timings) - return -ENOENT; - - found = 0; -#ifdef CONFIG_OMAP2_DSS_VENC - if (strncmp("pal", buf, 3) == 0) { - t = omap_dss_pal_timings; - found = 1; - } else if (strncmp("ntsc", buf, 4) == 0) { - t = omap_dss_ntsc_timings; - found = 1; - } -#endif - if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu", - &t.pixelclock, - &t.x_res, &t.hfp, &t.hbp, &t.hsw, - &t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9) - return -EINVAL; - - r = dssdev->driver->check_timings(dssdev, &t); - if (r) - return r; - - dssdev->driver->disable(dssdev); - dssdev->driver->set_timings(dssdev, &t); - r = dssdev->driver->enable(dssdev); - if (r) - return r; - - return size; -} - -static ssize_t display_rotate_show(struct omap_dss_device *dssdev, char *buf) -{ - int rotate; - if (!dssdev->driver->get_rotate) - return -ENOENT; - rotate = dssdev->driver->get_rotate(dssdev); - return snprintf(buf, PAGE_SIZE, "%u\n", rotate); -} - -static ssize_t display_rotate_store(struct omap_dss_device *dssdev, - const char *buf, size_t size) -{ - int rot, r; - - if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate) - return -ENOENT; - - r = kstrtoint(buf, 0, &rot); - if (r) - return r; - - r = dssdev->driver->set_rotate(dssdev, rot); - if (r) - return r; - - return size; -} - -static ssize_t display_mirror_show(struct omap_dss_device *dssdev, char *buf) -{ - int mirror; - if (!dssdev->driver->get_mirror) - return -ENOENT; - mirror = dssdev->driver->get_mirror(dssdev); - return snprintf(buf, PAGE_SIZE, "%u\n", mirror); -} - -static ssize_t display_mirror_store(struct omap_dss_device *dssdev, - const char *buf, size_t size) -{ - int r; - bool mirror; - - if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror) - return -ENOENT; - - r = strtobool(buf, &mirror); - if (r) - return r; - - r = dssdev->driver->set_mirror(dssdev, mirror); - if (r) - return r; - - return size; -} - -static ssize_t display_wss_show(struct omap_dss_device *dssdev, char *buf) -{ - unsigned int wss; - - if (!dssdev->driver->get_wss) - return -ENOENT; - - wss = dssdev->driver->get_wss(dssdev); - - return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss); -} - -static ssize_t display_wss_store(struct omap_dss_device *dssdev, - const char *buf, size_t size) -{ - u32 wss; - int r; - - if (!dssdev->driver->get_wss || !dssdev->driver->set_wss) - return -ENOENT; - - r = kstrtou32(buf, 0, &wss); - if (r) - return r; - - if (wss > 0xfffff) - return -EINVAL; - - r = dssdev->driver->set_wss(dssdev, wss); - if (r) - return r; - - return size; -} - -struct display_attribute { - struct attribute attr; - ssize_t (*show)(struct omap_dss_device *, char *); - ssize_t (*store)(struct omap_dss_device *, const char *, size_t); -}; - -#define DISPLAY_ATTR(_name, _mode, _show, _store) \ - struct display_attribute display_attr_##_name = \ - __ATTR(_name, _mode, _show, _store) - -static DISPLAY_ATTR(name, S_IRUGO, display_name_show, NULL); -static DISPLAY_ATTR(display_name, S_IRUGO, display_name_show, NULL); -static DISPLAY_ATTR(enabled, S_IRUGO|S_IWUSR, - display_enabled_show, display_enabled_store); -static DISPLAY_ATTR(tear_elim, S_IRUGO|S_IWUSR, - display_tear_show, display_tear_store); -static DISPLAY_ATTR(timings, S_IRUGO|S_IWUSR, - display_timings_show, display_timings_store); -static DISPLAY_ATTR(rotate, S_IRUGO|S_IWUSR, - display_rotate_show, display_rotate_store); -static DISPLAY_ATTR(mirror, S_IRUGO|S_IWUSR, - display_mirror_show, display_mirror_store); -static DISPLAY_ATTR(wss, S_IRUGO|S_IWUSR, - display_wss_show, display_wss_store); - -static struct attribute *display_sysfs_attrs[] = { - &display_attr_name.attr, - &display_attr_display_name.attr, - &display_attr_enabled.attr, - &display_attr_tear_elim.attr, - &display_attr_timings.attr, - &display_attr_rotate.attr, - &display_attr_mirror.attr, - &display_attr_wss.attr, - NULL -}; - -static ssize_t display_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct omap_dss_device *dssdev; - struct display_attribute *display_attr; - - dssdev = container_of(kobj, struct omap_dss_device, kobj); - display_attr = container_of(attr, struct display_attribute, attr); - - if (!display_attr->show) - return -ENOENT; - - return display_attr->show(dssdev, buf); -} - -static ssize_t display_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t size) -{ - struct omap_dss_device *dssdev; - struct display_attribute *display_attr; - - dssdev = container_of(kobj, struct omap_dss_device, kobj); - display_attr = container_of(attr, struct display_attribute, attr); - - if (!display_attr->store) - return -ENOENT; - - return display_attr->store(dssdev, buf, size); -} - -static const struct sysfs_ops display_sysfs_ops = { - .show = display_attr_show, - .store = display_attr_store, -}; - -static struct kobj_type display_ktype = { - .sysfs_ops = &display_sysfs_ops, - .default_attrs = display_sysfs_attrs, -}; - -int display_init_sysfs(struct platform_device *pdev) -{ - struct omap_dss_device *dssdev = NULL; - int r; - - for_each_dss_dev(dssdev) { - r = kobject_init_and_add(&dssdev->kobj, &display_ktype, - &pdev->dev.kobj, "%s", dssdev->alias); - if (r) { - DSSERR("failed to create sysfs files\n"); - omap_dss_put_device(dssdev); - goto err; - } - } - - return 0; - -err: - display_uninit_sysfs(pdev); - - return r; -} - -void display_uninit_sysfs(struct platform_device *pdev) -{ - struct omap_dss_device *dssdev = NULL; - - for_each_dss_dev(dssdev) { - if (kobject_name(&dssdev->kobj) == NULL) - continue; - - kobject_del(&dssdev->kobj); - kobject_put(&dssdev->kobj); - - memset(&dssdev->kobj, 0, sizeof(dssdev->kobj)); - } -} diff --git a/drivers/gpu/drm/omapdrm/dss/display.c b/drivers/gpu/drm/omapdrm/dss/display.c index ef5b9027985d..9f3dd09b0a6c 100644 --- a/drivers/gpu/drm/omapdrm/dss/display.c +++ b/drivers/gpu/drm/omapdrm/dss/display.c @@ -78,55 +78,6 @@ void omapdss_default_get_timings(struct omap_dss_device *dssdev, } EXPORT_SYMBOL(omapdss_default_get_timings); -int dss_suspend_all_devices(void) -{ - struct omap_dss_device *dssdev = NULL; - - for_each_dss_dev(dssdev) { - if (!dssdev->driver) - continue; - - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { - dssdev->driver->disable(dssdev); - dssdev->activate_after_resume = true; - } else { - dssdev->activate_after_resume = false; - } - } - - return 0; -} - -int dss_resume_all_devices(void) -{ - struct omap_dss_device *dssdev = NULL; - - for_each_dss_dev(dssdev) { - if (!dssdev->driver) - continue; - - if (dssdev->activate_after_resume) { - dssdev->driver->enable(dssdev); - dssdev->activate_after_resume = false; - } - } - - return 0; -} - -void dss_disable_all_devices(void) -{ - struct omap_dss_device *dssdev = NULL; - - for_each_dss_dev(dssdev) { - if (!dssdev->driver) - continue; - - if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) - dssdev->driver->disable(dssdev); - } -} - static LIST_HEAD(panel_list); static DEFINE_MUTEX(panel_list_mutex); static int disp_num_counter; diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index 7953e6a52346..97ea60257884 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -334,7 +334,7 @@ static int dpi_set_dispc_clk(struct dpi_data *dpi, unsigned long pck_req, static int dpi_set_mode(struct dpi_data *dpi) { struct omap_dss_device *out = &dpi->output; - struct omap_overlay_manager *mgr = out->manager; + enum omap_channel channel = out->dispc_channel; struct omap_video_timings *t = &dpi->timings; int lck_div = 0, pck_div = 0; unsigned long fck = 0; @@ -342,7 +342,7 @@ static int dpi_set_mode(struct dpi_data *dpi) int r = 0; if (dpi->pll) - r = dpi_set_dsi_clk(dpi, mgr->id, t->pixelclock, &fck, + r = dpi_set_dsi_clk(dpi, channel, t->pixelclock, &fck, &lck_div, &pck_div); else r = dpi_set_dispc_clk(dpi, t->pixelclock, &fck, @@ -359,7 +359,7 @@ static int dpi_set_mode(struct dpi_data *dpi) t->pixelclock = pck; } - dss_mgr_set_timings(mgr, t); + dss_mgr_set_timings(channel, t); return 0; } @@ -367,7 +367,7 @@ static int dpi_set_mode(struct dpi_data *dpi) static void dpi_config_lcd_manager(struct dpi_data *dpi) { struct omap_dss_device *out = &dpi->output; - struct omap_overlay_manager *mgr = out->manager; + enum omap_channel channel = out->dispc_channel; dpi->mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; @@ -378,13 +378,14 @@ static void dpi_config_lcd_manager(struct dpi_data *dpi) dpi->mgr_config.lcden_sig_polarity = 0; - dss_mgr_set_lcd_config(mgr, &dpi->mgr_config); + dss_mgr_set_lcd_config(channel, &dpi->mgr_config); } static int dpi_display_enable(struct omap_dss_device *dssdev) { struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); struct omap_dss_device *out = &dpi->output; + enum omap_channel channel = out->dispc_channel; int r; mutex_lock(&dpi->lock); @@ -395,7 +396,7 @@ static int dpi_display_enable(struct omap_dss_device *dssdev) goto err_no_reg; } - if (out->manager == NULL) { + if (!out->dispc_channel_connected) { DSSERR("failed to enable display: no output/manager\n"); r = -ENODEV; goto err_no_out_mgr; @@ -411,7 +412,7 @@ static int dpi_display_enable(struct omap_dss_device *dssdev) if (r) goto err_get_dispc; - r = dss_dpi_select_source(out->port_num, out->manager->id); + r = dss_dpi_select_source(out->port_num, channel); if (r) goto err_src_sel; @@ -429,7 +430,7 @@ static int dpi_display_enable(struct omap_dss_device *dssdev) mdelay(2); - r = dss_mgr_enable(out->manager); + r = dss_mgr_enable(channel); if (r) goto err_mgr_enable; @@ -457,14 +458,14 @@ err_no_reg: static void dpi_display_disable(struct omap_dss_device *dssdev) { struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - struct omap_overlay_manager *mgr = dpi->output.manager; + enum omap_channel channel = dpi->output.dispc_channel; mutex_lock(&dpi->lock); - dss_mgr_disable(mgr); + dss_mgr_disable(channel); if (dpi->pll) { - dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK); + dss_select_lcd_clk_source(channel, OMAP_DSS_CLK_SRC_FCK); dss_pll_disable(dpi->pll); } @@ -506,14 +507,17 @@ static int dpi_check_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - struct omap_overlay_manager *mgr = dpi->output.manager; + enum omap_channel channel = dpi->output.dispc_channel; int lck_div, pck_div; unsigned long fck; unsigned long pck; struct dpi_clk_calc_ctx ctx; bool ok; - if (mgr && !dispc_mgr_timings_ok(mgr->id, timings)) + if (timings->x_res % 8 != 0) + return -EINVAL; + + if (!dispc_mgr_timings_ok(channel, timings)) return -EINVAL; if (timings->pixelclock == 0) @@ -660,7 +664,7 @@ static int dpi_connect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - struct omap_overlay_manager *mgr; + enum omap_channel channel = dpi->output.dispc_channel; int r; r = dpi_init_regulator(dpi); @@ -669,11 +673,7 @@ static int dpi_connect(struct omap_dss_device *dssdev, dpi_init_pll(dpi); - mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); - if (!mgr) - return -ENODEV; - - r = dss_mgr_connect(mgr, dssdev); + r = dss_mgr_connect(channel, dssdev); if (r) return r; @@ -681,7 +681,7 @@ static int dpi_connect(struct omap_dss_device *dssdev, if (r) { DSSERR("failed to connect output to new device: %s\n", dst->name); - dss_mgr_disconnect(mgr, dssdev); + dss_mgr_disconnect(channel, dssdev); return r; } @@ -691,6 +691,9 @@ static int dpi_connect(struct omap_dss_device *dssdev, static void dpi_disconnect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { + struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); + enum omap_channel channel = dpi->output.dispc_channel; + WARN_ON(dst != dssdev->dst); if (dst != dssdev->dst) @@ -698,8 +701,7 @@ static void dpi_disconnect(struct omap_dss_device *dssdev, omapdss_output_unset_device(dssdev); - if (dssdev->manager) - dss_mgr_disconnect(dssdev->manager, dssdev); + dss_mgr_disconnect(channel, dssdev); } static const struct omapdss_dpi_ops dpi_ops = { diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index 43be4b2a7b05..8730646a0cbb 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -214,9 +214,9 @@ struct dsi_reg { u16 module; u16 idx; }; typedef void (*omap_dsi_isr_t) (void *arg, u32 mask); static int dsi_display_init_dispc(struct platform_device *dsidev, - struct omap_overlay_manager *mgr); + enum omap_channel channel); static void dsi_display_uninit_dispc(struct platform_device *dsidev, - struct omap_overlay_manager *mgr); + enum omap_channel channel); static int dsi_vc_send_null(struct omap_dss_device *dssdev, int channel); @@ -3826,19 +3826,19 @@ static int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - struct omap_overlay_manager *mgr = dsi->output.manager; + enum omap_channel dispc_channel = dssdev->dispc_channel; int bpp = dsi_get_pixel_size(dsi->pix_fmt); struct omap_dss_device *out = &dsi->output; u8 data_type; u16 word_count; int r; - if (out->manager == NULL) { + if (!out->dispc_channel_connected) { DSSERR("failed to enable display: no output/manager\n"); return -ENODEV; } - r = dsi_display_init_dispc(dsidev, mgr); + r = dsi_display_init_dispc(dsidev, dispc_channel); if (r) goto err_init_dispc; @@ -3876,7 +3876,7 @@ static int dsi_enable_video_output(struct omap_dss_device *dssdev, int channel) dsi_if_enable(dsidev, true); } - r = dss_mgr_enable(mgr); + r = dss_mgr_enable(dispc_channel); if (r) goto err_mgr_enable; @@ -3888,7 +3888,7 @@ err_mgr_enable: dsi_vc_enable(dsidev, channel, false); } err_pix_fmt: - dsi_display_uninit_dispc(dsidev, mgr); + dsi_display_uninit_dispc(dsidev, dispc_channel); err_init_dispc: return r; } @@ -3897,7 +3897,7 @@ static void dsi_disable_video_output(struct omap_dss_device *dssdev, int channel { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - struct omap_overlay_manager *mgr = dsi->output.manager; + enum omap_channel dispc_channel = dssdev->dispc_channel; if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { dsi_if_enable(dsidev, false); @@ -3910,15 +3910,15 @@ static void dsi_disable_video_output(struct omap_dss_device *dssdev, int channel dsi_if_enable(dsidev, true); } - dss_mgr_disable(mgr); + dss_mgr_disable(dispc_channel); - dsi_display_uninit_dispc(dsidev, mgr); + dsi_display_uninit_dispc(dsidev, dispc_channel); } static void dsi_update_screen_dispc(struct platform_device *dsidev) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); - struct omap_overlay_manager *mgr = dsi->output.manager; + enum omap_channel dispc_channel = dsi->output.dispc_channel; unsigned bytespp; unsigned bytespl; unsigned bytespf; @@ -3980,9 +3980,9 @@ static void dsi_update_screen_dispc(struct platform_device *dsidev) msecs_to_jiffies(250)); BUG_ON(r == 0); - dss_mgr_set_timings(mgr, &dsi->timings); + dss_mgr_set_timings(dispc_channel, &dsi->timings); - dss_mgr_start_update(mgr); + dss_mgr_start_update(dispc_channel); if (dsi->te_enabled) { /* disable LP_RX_TO, so that we can receive TE. Time to wait @@ -4105,17 +4105,17 @@ static int dsi_configure_dispc_clocks(struct platform_device *dsidev) } static int dsi_display_init_dispc(struct platform_device *dsidev, - struct omap_overlay_manager *mgr) + enum omap_channel channel) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); int r; - dss_select_lcd_clk_source(mgr->id, dsi->module_id == 0 ? + dss_select_lcd_clk_source(channel, dsi->module_id == 0 ? OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC : OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC); if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) { - r = dss_mgr_register_framedone_handler(mgr, + r = dss_mgr_register_framedone_handler(channel, dsi_framedone_irq_callback, dsidev); if (r) { DSSERR("can't register FRAMEDONE handler\n"); @@ -4140,7 +4140,7 @@ static int dsi_display_init_dispc(struct platform_device *dsidev, dsi->timings.de_level = OMAPDSS_SIG_ACTIVE_HIGH; dsi->timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE; - dss_mgr_set_timings(mgr, &dsi->timings); + dss_mgr_set_timings(channel, &dsi->timings); r = dsi_configure_dispc_clocks(dsidev); if (r) @@ -4151,28 +4151,28 @@ static int dsi_display_init_dispc(struct platform_device *dsidev, dsi_get_pixel_size(dsi->pix_fmt); dsi->mgr_config.lcden_sig_polarity = 0; - dss_mgr_set_lcd_config(mgr, &dsi->mgr_config); + dss_mgr_set_lcd_config(channel, &dsi->mgr_config); return 0; err1: if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) - dss_mgr_unregister_framedone_handler(mgr, + dss_mgr_unregister_framedone_handler(channel, dsi_framedone_irq_callback, dsidev); err: - dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK); + dss_select_lcd_clk_source(channel, OMAP_DSS_CLK_SRC_FCK); return r; } static void dsi_display_uninit_dispc(struct platform_device *dsidev, - struct omap_overlay_manager *mgr) + enum omap_channel channel) { struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); if (dsi->mode == OMAP_DSS_DSI_CMD_MODE) - dss_mgr_unregister_framedone_handler(mgr, + dss_mgr_unregister_framedone_handler(channel, dsi_framedone_irq_callback, dsidev); - dss_select_lcd_clk_source(mgr->id, OMAP_DSS_CLK_SRC_FCK); + dss_select_lcd_clk_source(channel, OMAP_DSS_CLK_SRC_FCK); } static int dsi_configure_dsi_clocks(struct platform_device *dsidev) @@ -4983,18 +4983,14 @@ static int dsi_connect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); - struct omap_overlay_manager *mgr; + enum omap_channel dispc_channel = dssdev->dispc_channel; int r; r = dsi_regulator_init(dsidev); if (r) return r; - mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); - if (!mgr) - return -ENODEV; - - r = dss_mgr_connect(mgr, dssdev); + r = dss_mgr_connect(dispc_channel, dssdev); if (r) return r; @@ -5002,7 +4998,7 @@ static int dsi_connect(struct omap_dss_device *dssdev, if (r) { DSSERR("failed to connect output to new device: %s\n", dssdev->name); - dss_mgr_disconnect(mgr, dssdev); + dss_mgr_disconnect(dispc_channel, dssdev); return r; } @@ -5012,6 +5008,8 @@ static int dsi_connect(struct omap_dss_device *dssdev, static void dsi_disconnect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { + enum omap_channel dispc_channel = dssdev->dispc_channel; + WARN_ON(dst != dssdev->dst); if (dst != dssdev->dst) @@ -5019,8 +5017,7 @@ static void dsi_disconnect(struct omap_dss_device *dssdev, omapdss_output_unset_device(dssdev); - if (dssdev->manager) - dss_mgr_disconnect(dssdev->manager, dssdev); + dss_mgr_disconnect(dispc_channel, dssdev); } static const struct omapdss_dsi_ops dsi_ops = { diff --git a/drivers/gpu/drm/omapdrm/dss/dss.h b/drivers/gpu/drm/omapdrm/dss/dss.h index 9a6453235585..38e6ab50142d 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.h +++ b/drivers/gpu/drm/omapdrm/dss/dss.h @@ -25,6 +25,8 @@ #include <linux/interrupt.h> +#include "omapdss.h" + #ifdef pr_fmt #undef pr_fmt #endif @@ -205,29 +207,6 @@ void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask); int dss_set_min_bus_tput(struct device *dev, unsigned long tput); int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *)); -/* display */ -int dss_suspend_all_devices(void); -int dss_resume_all_devices(void); -void dss_disable_all_devices(void); - -int display_init_sysfs(struct platform_device *pdev); -void display_uninit_sysfs(struct platform_device *pdev); - -/* manager */ -int dss_init_overlay_managers(void); -void dss_uninit_overlay_managers(void); -int dss_init_overlay_managers_sysfs(struct platform_device *pdev); -void dss_uninit_overlay_managers_sysfs(struct platform_device *pdev); -int dss_mgr_simple_check(struct omap_overlay_manager *mgr, - const struct omap_overlay_manager_info *info); -int dss_mgr_check_timings(struct omap_overlay_manager *mgr, - const struct omap_video_timings *timings); -int dss_mgr_check(struct omap_overlay_manager *mgr, - struct omap_overlay_manager_info *info, - const struct omap_video_timings *mgr_timings, - const struct dss_lcd_mgr_config *config, - struct omap_overlay_info **overlay_infos); - static inline bool dss_mgr_is_lcd(enum omap_channel id) { if (id == OMAP_DSS_CHANNEL_LCD || id == OMAP_DSS_CHANNEL_LCD2 || @@ -237,24 +216,6 @@ static inline bool dss_mgr_is_lcd(enum omap_channel id) return false; } -int dss_manager_kobj_init(struct omap_overlay_manager *mgr, - struct platform_device *pdev); -void dss_manager_kobj_uninit(struct omap_overlay_manager *mgr); - -/* overlay */ -void dss_init_overlays(struct platform_device *pdev); -void dss_uninit_overlays(struct platform_device *pdev); -void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr); -int dss_ovl_simple_check(struct omap_overlay *ovl, - const struct omap_overlay_info *info); -int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info, - const struct omap_video_timings *mgr_timings); -bool dss_ovl_use_replication(struct dss_lcd_mgr_config config, - enum omap_color_mode mode); -int dss_overlay_kobj_init(struct omap_overlay *ovl, - struct platform_device *pdev); -void dss_overlay_kobj_uninit(struct omap_overlay *ovl); - /* DSS */ int dss_init_platform_driver(void) __init; void dss_uninit_platform_driver(void); diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index 7103c659a534..f892ae157ff3 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -165,9 +165,10 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) { int r; struct omap_video_timings *p; - struct omap_overlay_manager *mgr = hdmi.output.manager; + enum omap_channel channel = dssdev->dispc_channel; struct hdmi_wp_data *wp = &hdmi.wp; struct dss_pll_clock_info hdmi_cinfo = { 0 }; + unsigned pc; r = hdmi_power_on_core(dssdev); if (r) @@ -181,7 +182,11 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); - hdmi_pll_compute(&hdmi.pll, p->pixelclock, &hdmi_cinfo); + pc = p->pixelclock; + if (p->double_pixel) + pc *= 2; + + hdmi_pll_compute(&hdmi.pll, pc, &hdmi_cinfo); r = dss_pll_enable(&hdmi.pll.pll); if (r) { @@ -212,24 +217,24 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) dispc_enable_gamma_table(0); /* tv size */ - dss_mgr_set_timings(mgr, p); + dss_mgr_set_timings(channel, p); - r = hdmi_wp_video_start(&hdmi.wp); + r = dss_mgr_enable(channel); if (r) - goto err_vid_enable; + goto err_mgr_enable; - r = dss_mgr_enable(mgr); + r = hdmi_wp_video_start(&hdmi.wp); if (r) - goto err_mgr_enable; + goto err_vid_enable; hdmi_wp_set_irqenable(wp, HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); return 0; -err_mgr_enable: - hdmi_wp_video_stop(&hdmi.wp); err_vid_enable: + dss_mgr_disable(channel); +err_mgr_enable: hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); err_phy_pwr: err_phy_cfg: @@ -242,14 +247,14 @@ err_pll_enable: static void hdmi_power_off_full(struct omap_dss_device *dssdev) { - struct omap_overlay_manager *mgr = hdmi.output.manager; + enum omap_channel channel = dssdev->dispc_channel; hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); - dss_mgr_disable(mgr); - hdmi_wp_video_stop(&hdmi.wp); + dss_mgr_disable(channel); + hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); dss_pll_disable(&hdmi.pll.pll); @@ -260,9 +265,7 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev) static int hdmi_display_check_timing(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - struct omap_dss_device *out = &hdmi.output; - - if (!dispc_mgr_timings_ok(out->dispc_channel, timings)) + if (!dispc_mgr_timings_ok(dssdev->dispc_channel, timings)) return -EINVAL; return 0; @@ -343,7 +346,7 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev) mutex_lock(&hdmi.lock); - if (out->manager == NULL) { + if (!out->dispc_channel_connected) { DSSERR("failed to enable display: no output/manager\n"); r = -ENODEV; goto err0; @@ -433,18 +436,14 @@ static void hdmi_core_disable(struct omap_dss_device *dssdev) static int hdmi_connect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { - struct omap_overlay_manager *mgr; + enum omap_channel channel = dssdev->dispc_channel; int r; r = hdmi_init_regulator(); if (r) return r; - mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); - if (!mgr) - return -ENODEV; - - r = dss_mgr_connect(mgr, dssdev); + r = dss_mgr_connect(channel, dssdev); if (r) return r; @@ -452,7 +451,7 @@ static int hdmi_connect(struct omap_dss_device *dssdev, if (r) { DSSERR("failed to connect output to new device: %s\n", dst->name); - dss_mgr_disconnect(mgr, dssdev); + dss_mgr_disconnect(channel, dssdev); return r; } @@ -462,6 +461,8 @@ static int hdmi_connect(struct omap_dss_device *dssdev, static void hdmi_disconnect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { + enum omap_channel channel = dssdev->dispc_channel; + WARN_ON(dst != dssdev->dst); if (dst != dssdev->dst) @@ -469,8 +470,7 @@ static void hdmi_disconnect(struct omap_dss_device *dssdev, omapdss_output_unset_device(dssdev); - if (dssdev->manager) - dss_mgr_disconnect(dssdev->manager, dssdev); + dss_mgr_disconnect(channel, dssdev); } static int hdmi_read_edid(struct omap_dss_device *dssdev, diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index a955a2c4c061..a43f7b10e113 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -182,8 +182,9 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) { int r; struct omap_video_timings *p; - struct omap_overlay_manager *mgr = hdmi.output.manager; + enum omap_channel channel = dssdev->dispc_channel; struct dss_pll_clock_info hdmi_cinfo = { 0 }; + unsigned pc; r = hdmi_power_on_core(dssdev); if (r) @@ -193,7 +194,11 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res); - hdmi_pll_compute(&hdmi.pll, p->pixelclock, &hdmi_cinfo); + pc = p->pixelclock; + if (p->double_pixel) + pc *= 2; + + hdmi_pll_compute(&hdmi.pll, pc, &hdmi_cinfo); /* disable and clear irqs */ hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); @@ -229,24 +234,24 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev) dispc_enable_gamma_table(0); /* tv size */ - dss_mgr_set_timings(mgr, p); + dss_mgr_set_timings(channel, p); - r = hdmi_wp_video_start(&hdmi.wp); + r = dss_mgr_enable(channel); if (r) - goto err_vid_enable; + goto err_mgr_enable; - r = dss_mgr_enable(mgr); + r = hdmi_wp_video_start(&hdmi.wp); if (r) - goto err_mgr_enable; + goto err_vid_enable; hdmi_wp_set_irqenable(&hdmi.wp, HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT); return 0; -err_mgr_enable: - hdmi_wp_video_stop(&hdmi.wp); err_vid_enable: + dss_mgr_disable(channel); +err_mgr_enable: hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); err_phy_pwr: err_phy_cfg: @@ -259,14 +264,14 @@ err_pll_enable: static void hdmi_power_off_full(struct omap_dss_device *dssdev) { - struct omap_overlay_manager *mgr = hdmi.output.manager; + enum omap_channel channel = dssdev->dispc_channel; hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff); - dss_mgr_disable(mgr); - hdmi_wp_video_stop(&hdmi.wp); + dss_mgr_disable(channel); + hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF); dss_pll_disable(&hdmi.pll.pll); @@ -277,13 +282,7 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev) static int hdmi_display_check_timing(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - struct omap_dss_device *out = &hdmi.output; - - /* TODO: proper interlace support */ - if (timings->interlace) - return -EINVAL; - - if (!dispc_mgr_timings_ok(out->dispc_channel, timings)) + if (!dispc_mgr_timings_ok(dssdev->dispc_channel, timings)) return -EINVAL; return 0; @@ -373,7 +372,7 @@ static int hdmi_display_enable(struct omap_dss_device *dssdev) mutex_lock(&hdmi.lock); - if (out->manager == NULL) { + if (!out->dispc_channel_connected) { DSSERR("failed to enable display: no output/manager\n"); r = -ENODEV; goto err0; @@ -463,18 +462,14 @@ static void hdmi_core_disable(struct omap_dss_device *dssdev) static int hdmi_connect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { - struct omap_overlay_manager *mgr; + enum omap_channel channel = dssdev->dispc_channel; int r; r = hdmi_init_regulator(); if (r) return r; - mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); - if (!mgr) - return -ENODEV; - - r = dss_mgr_connect(mgr, dssdev); + r = dss_mgr_connect(channel, dssdev); if (r) return r; @@ -482,7 +477,7 @@ static int hdmi_connect(struct omap_dss_device *dssdev, if (r) { DSSERR("failed to connect output to new device: %s\n", dst->name); - dss_mgr_disconnect(mgr, dssdev); + dss_mgr_disconnect(channel, dssdev); return r; } @@ -492,6 +487,8 @@ static int hdmi_connect(struct omap_dss_device *dssdev, static void hdmi_disconnect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { + enum omap_channel channel = dssdev->dispc_channel; + WARN_ON(dst != dssdev->dst); if (dst != dssdev->dst) @@ -499,8 +496,7 @@ static void hdmi_disconnect(struct omap_dss_device *dssdev, omapdss_output_unset_device(dssdev); - if (dssdev->manager) - dss_mgr_disconnect(dssdev->manager, dssdev); + dss_mgr_disconnect(channel, dssdev); } static int hdmi_read_edid(struct omap_dss_device *dssdev, diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c index 8ea531d2652c..6a397520cae5 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c @@ -292,25 +292,36 @@ static void hdmi_core_init(struct hdmi_core_vid_config *video_cfg, { DSSDBG("hdmi_core_init\n"); + video_cfg->v_fc_config.timings = cfg->timings; + /* video core */ video_cfg->data_enable_pol = 1; /* It is always 1*/ - video_cfg->v_fc_config.timings.hsync_level = cfg->timings.hsync_level; - video_cfg->v_fc_config.timings.x_res = cfg->timings.x_res; - video_cfg->v_fc_config.timings.hsw = cfg->timings.hsw - 1; - video_cfg->v_fc_config.timings.hbp = cfg->timings.hbp; - video_cfg->v_fc_config.timings.hfp = cfg->timings.hfp; video_cfg->hblank = cfg->timings.hfp + - cfg->timings.hbp + cfg->timings.hsw - 1; - video_cfg->v_fc_config.timings.vsync_level = cfg->timings.vsync_level; - video_cfg->v_fc_config.timings.y_res = cfg->timings.y_res; - video_cfg->v_fc_config.timings.vsw = cfg->timings.vsw; - video_cfg->v_fc_config.timings.vfp = cfg->timings.vfp; - video_cfg->v_fc_config.timings.vbp = cfg->timings.vbp; - video_cfg->vblank_osc = 0; /* Always 0 - need to confirm */ + cfg->timings.hbp + cfg->timings.hsw; + video_cfg->vblank_osc = 0; video_cfg->vblank = cfg->timings.vsw + cfg->timings.vfp + cfg->timings.vbp; video_cfg->v_fc_config.hdmi_dvi_mode = cfg->hdmi_dvi_mode; - video_cfg->v_fc_config.timings.interlace = cfg->timings.interlace; + + if (cfg->timings.interlace) { + /* set vblank_osc if vblank is fractional */ + if (video_cfg->vblank % 2 != 0) + video_cfg->vblank_osc = 1; + + video_cfg->v_fc_config.timings.y_res /= 2; + video_cfg->vblank /= 2; + video_cfg->v_fc_config.timings.vfp /= 2; + video_cfg->v_fc_config.timings.vsw /= 2; + video_cfg->v_fc_config.timings.vbp /= 2; + } + + if (cfg->timings.double_pixel) { + video_cfg->v_fc_config.timings.x_res *= 2; + video_cfg->hblank *= 2; + video_cfg->v_fc_config.timings.hfp *= 2; + video_cfg->v_fc_config.timings.hsw *= 2; + video_cfg->v_fc_config.timings.hbp *= 2; + } } /* DSS_HDMI_CORE_VIDEO_CONFIG */ @@ -377,6 +388,11 @@ static void hdmi_core_video_config(struct hdmi_core_data *core, /* select DVI mode */ REG_FLD_MOD(base, HDMI_CORE_FC_INVIDCONF, cfg->v_fc_config.hdmi_dvi_mode, 3, 3); + + if (cfg->v_fc_config.timings.double_pixel) + REG_FLD_MOD(base, HDMI_CORE_FC_PRCONF, 2, 7, 4); + else + REG_FLD_MOD(base, HDMI_CORE_FC_PRCONF, 1, 7, 4); } static void hdmi_core_config_video_packetizer(struct hdmi_core_data *core) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c b/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c index 7c544bc56fb5..13442b9052d1 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c @@ -165,12 +165,24 @@ void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp, { u32 timing_h = 0; u32 timing_v = 0; + unsigned hsw_offset = 1; DSSDBG("Enter hdmi_wp_video_config_timing\n"); + /* + * On OMAP4 and OMAP5 ES1 the HSW field is programmed as is. On OMAP5 + * ES2+ (including DRA7/AM5 SoCs) HSW field is programmed to hsw-1. + * However, we don't support OMAP5 ES1 at all, so we can just check for + * OMAP4 here. + */ + if (omapdss_get_version() == OMAPDSS_VER_OMAP4430_ES1 || + omapdss_get_version() == OMAPDSS_VER_OMAP4430_ES2 || + omapdss_get_version() == OMAPDSS_VER_OMAP4) + hsw_offset = 0; + timing_h |= FLD_VAL(timings->hbp, 31, 20); timing_h |= FLD_VAL(timings->hfp, 19, 8); - timing_h |= FLD_VAL(timings->hsw, 7, 0); + timing_h |= FLD_VAL(timings->hsw - hsw_offset, 7, 0); hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_H, timing_h); timing_v |= FLD_VAL(timings->vbp, 31, 20); @@ -187,8 +199,6 @@ void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt, video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444; video_fmt->y_res = param->timings.y_res; video_fmt->x_res = param->timings.x_res; - if (param->timings.interlace) - video_fmt->y_res /= 2; timings->hbp = param->timings.hbp; timings->hfp = param->timings.hfp; @@ -196,9 +206,25 @@ void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt, timings->vbp = param->timings.vbp; timings->vfp = param->timings.vfp; timings->vsw = param->timings.vsw; + timings->vsync_level = param->timings.vsync_level; timings->hsync_level = param->timings.hsync_level; timings->interlace = param->timings.interlace; + timings->double_pixel = param->timings.double_pixel; + + if (param->timings.interlace) { + video_fmt->y_res /= 2; + timings->vbp /= 2; + timings->vfp /= 2; + timings->vsw /= 2; + } + + if (param->timings.double_pixel) { + video_fmt->x_res *= 2; + timings->hfp *= 2; + timings->hsw *= 2; + timings->hbp *= 2; + } } void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp, diff --git a/drivers/gpu/drm/omapdrm/dss/manager-sysfs.c b/drivers/gpu/drm/omapdrm/dss/manager-sysfs.c deleted file mode 100644 index a7414fb12830..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/manager-sysfs.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> - * - * Some code and ideas taken from drivers/video/omap/ driver - * by Imre Deak. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#define DSS_SUBSYS_NAME "MANAGER" - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/jiffies.h> - -#include <video/omapdss.h> - -#include "dss.h" -#include "dss_features.h" - -static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name); -} - -static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf) -{ - struct omap_dss_device *dssdev = mgr->get_device(mgr); - - return snprintf(buf, PAGE_SIZE, "%s\n", dssdev ? - dssdev->name : "<none>"); -} - -static int manager_display_match(struct omap_dss_device *dssdev, void *data) -{ - const char *str = data; - - return sysfs_streq(dssdev->name, str); -} - -static ssize_t manager_display_store(struct omap_overlay_manager *mgr, - const char *buf, size_t size) -{ - int r = 0; - size_t len = size; - struct omap_dss_device *dssdev = NULL; - struct omap_dss_device *old_dssdev; - - if (buf[size-1] == '\n') - --len; - - if (len > 0) - dssdev = omap_dss_find_device((void *)buf, - manager_display_match); - - if (len > 0 && dssdev == NULL) - return -EINVAL; - - if (dssdev) { - DSSDBG("display %s found\n", dssdev->name); - - if (omapdss_device_is_connected(dssdev)) { - DSSERR("new display is already connected\n"); - r = -EINVAL; - goto put_device; - } - - if (omapdss_device_is_enabled(dssdev)) { - DSSERR("new display is not disabled\n"); - r = -EINVAL; - goto put_device; - } - } - - old_dssdev = mgr->get_device(mgr); - if (old_dssdev) { - if (omapdss_device_is_enabled(old_dssdev)) { - DSSERR("old display is not disabled\n"); - r = -EINVAL; - goto put_device; - } - - old_dssdev->driver->disconnect(old_dssdev); - } - - if (dssdev) { - r = dssdev->driver->connect(dssdev); - if (r) { - DSSERR("failed to connect new device\n"); - goto put_device; - } - - old_dssdev = mgr->get_device(mgr); - if (old_dssdev != dssdev) { - DSSERR("failed to connect device to this manager\n"); - dssdev->driver->disconnect(dssdev); - goto put_device; - } - - r = mgr->apply(mgr); - if (r) { - DSSERR("failed to apply dispc config\n"); - goto put_device; - } - } - -put_device: - if (dssdev) - omap_dss_put_device(dssdev); - - return r ? r : size; -} - -static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr, - char *buf) -{ - struct omap_overlay_manager_info info; - - mgr->get_manager_info(mgr, &info); - - return snprintf(buf, PAGE_SIZE, "%#x\n", info.default_color); -} - -static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr, - const char *buf, size_t size) -{ - struct omap_overlay_manager_info info; - u32 color; - int r; - - r = kstrtouint(buf, 0, &color); - if (r) - return r; - - mgr->get_manager_info(mgr, &info); - - info.default_color = color; - - r = mgr->set_manager_info(mgr, &info); - if (r) - return r; - - r = mgr->apply(mgr); - if (r) - return r; - - return size; -} - -static const char *trans_key_type_str[] = { - "gfx-destination", - "video-source", -}; - -static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr, - char *buf) -{ - enum omap_dss_trans_key_type key_type; - struct omap_overlay_manager_info info; - - mgr->get_manager_info(mgr, &info); - - key_type = info.trans_key_type; - BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str)); - - return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]); -} - -static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr, - const char *buf, size_t size) -{ - enum omap_dss_trans_key_type key_type; - struct omap_overlay_manager_info info; - int r; - - for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST; - key_type < ARRAY_SIZE(trans_key_type_str); key_type++) { - if (sysfs_streq(buf, trans_key_type_str[key_type])) - break; - } - - if (key_type == ARRAY_SIZE(trans_key_type_str)) - return -EINVAL; - - mgr->get_manager_info(mgr, &info); - - info.trans_key_type = key_type; - - r = mgr->set_manager_info(mgr, &info); - if (r) - return r; - - r = mgr->apply(mgr); - if (r) - return r; - - return size; -} - -static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr, - char *buf) -{ - struct omap_overlay_manager_info info; - - mgr->get_manager_info(mgr, &info); - - return snprintf(buf, PAGE_SIZE, "%#x\n", info.trans_key); -} - -static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr, - const char *buf, size_t size) -{ - struct omap_overlay_manager_info info; - u32 key_value; - int r; - - r = kstrtouint(buf, 0, &key_value); - if (r) - return r; - - mgr->get_manager_info(mgr, &info); - - info.trans_key = key_value; - - r = mgr->set_manager_info(mgr, &info); - if (r) - return r; - - r = mgr->apply(mgr); - if (r) - return r; - - return size; -} - -static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr, - char *buf) -{ - struct omap_overlay_manager_info info; - - mgr->get_manager_info(mgr, &info); - - return snprintf(buf, PAGE_SIZE, "%d\n", info.trans_enabled); -} - -static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr, - const char *buf, size_t size) -{ - struct omap_overlay_manager_info info; - bool enable; - int r; - - r = strtobool(buf, &enable); - if (r) - return r; - - mgr->get_manager_info(mgr, &info); - - info.trans_enabled = enable; - - r = mgr->set_manager_info(mgr, &info); - if (r) - return r; - - r = mgr->apply(mgr); - if (r) - return r; - - return size; -} - -static ssize_t manager_alpha_blending_enabled_show( - struct omap_overlay_manager *mgr, char *buf) -{ - struct omap_overlay_manager_info info; - - if(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) - return -ENODEV; - - mgr->get_manager_info(mgr, &info); - - return snprintf(buf, PAGE_SIZE, "%d\n", - info.partial_alpha_enabled); -} - -static ssize_t manager_alpha_blending_enabled_store( - struct omap_overlay_manager *mgr, - const char *buf, size_t size) -{ - struct omap_overlay_manager_info info; - bool enable; - int r; - - if(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) - return -ENODEV; - - r = strtobool(buf, &enable); - if (r) - return r; - - mgr->get_manager_info(mgr, &info); - - info.partial_alpha_enabled = enable; - - r = mgr->set_manager_info(mgr, &info); - if (r) - return r; - - r = mgr->apply(mgr); - if (r) - return r; - - return size; -} - -static ssize_t manager_cpr_enable_show(struct omap_overlay_manager *mgr, - char *buf) -{ - struct omap_overlay_manager_info info; - - mgr->get_manager_info(mgr, &info); - - return snprintf(buf, PAGE_SIZE, "%d\n", info.cpr_enable); -} - -static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr, - const char *buf, size_t size) -{ - struct omap_overlay_manager_info info; - int r; - bool enable; - - if (!dss_has_feature(FEAT_CPR)) - return -ENODEV; - - r = strtobool(buf, &enable); - if (r) - return r; - - mgr->get_manager_info(mgr, &info); - - if (info.cpr_enable == enable) - return size; - - info.cpr_enable = enable; - - r = mgr->set_manager_info(mgr, &info); - if (r) - return r; - - r = mgr->apply(mgr); - if (r) - return r; - - return size; -} - -static ssize_t manager_cpr_coef_show(struct omap_overlay_manager *mgr, - char *buf) -{ - struct omap_overlay_manager_info info; - - mgr->get_manager_info(mgr, &info); - - return snprintf(buf, PAGE_SIZE, - "%d %d %d %d %d %d %d %d %d\n", - info.cpr_coefs.rr, - info.cpr_coefs.rg, - info.cpr_coefs.rb, - info.cpr_coefs.gr, - info.cpr_coefs.gg, - info.cpr_coefs.gb, - info.cpr_coefs.br, - info.cpr_coefs.bg, - info.cpr_coefs.bb); -} - -static ssize_t manager_cpr_coef_store(struct omap_overlay_manager *mgr, - const char *buf, size_t size) -{ - struct omap_overlay_manager_info info; - struct omap_dss_cpr_coefs coefs; - int r, i; - s16 *arr; - - if (!dss_has_feature(FEAT_CPR)) - return -ENODEV; - - if (sscanf(buf, "%hd %hd %hd %hd %hd %hd %hd %hd %hd", - &coefs.rr, &coefs.rg, &coefs.rb, - &coefs.gr, &coefs.gg, &coefs.gb, - &coefs.br, &coefs.bg, &coefs.bb) != 9) - return -EINVAL; - - arr = (s16[]){ coefs.rr, coefs.rg, coefs.rb, - coefs.gr, coefs.gg, coefs.gb, - coefs.br, coefs.bg, coefs.bb }; - - for (i = 0; i < 9; ++i) { - if (arr[i] < -512 || arr[i] > 511) - return -EINVAL; - } - - mgr->get_manager_info(mgr, &info); - - info.cpr_coefs = coefs; - - r = mgr->set_manager_info(mgr, &info); - if (r) - return r; - - r = mgr->apply(mgr); - if (r) - return r; - - return size; -} - -struct manager_attribute { - struct attribute attr; - ssize_t (*show)(struct omap_overlay_manager *, char *); - ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t); -}; - -#define MANAGER_ATTR(_name, _mode, _show, _store) \ - struct manager_attribute manager_attr_##_name = \ - __ATTR(_name, _mode, _show, _store) - -static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL); -static MANAGER_ATTR(display, S_IRUGO|S_IWUSR, - manager_display_show, manager_display_store); -static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR, - manager_default_color_show, manager_default_color_store); -static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR, - manager_trans_key_type_show, manager_trans_key_type_store); -static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR, - manager_trans_key_value_show, manager_trans_key_value_store); -static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR, - manager_trans_key_enabled_show, - manager_trans_key_enabled_store); -static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR, - manager_alpha_blending_enabled_show, - manager_alpha_blending_enabled_store); -static MANAGER_ATTR(cpr_enable, S_IRUGO|S_IWUSR, - manager_cpr_enable_show, - manager_cpr_enable_store); -static MANAGER_ATTR(cpr_coef, S_IRUGO|S_IWUSR, - manager_cpr_coef_show, - manager_cpr_coef_store); - - -static struct attribute *manager_sysfs_attrs[] = { - &manager_attr_name.attr, - &manager_attr_display.attr, - &manager_attr_default_color.attr, - &manager_attr_trans_key_type.attr, - &manager_attr_trans_key_value.attr, - &manager_attr_trans_key_enabled.attr, - &manager_attr_alpha_blending_enabled.attr, - &manager_attr_cpr_enable.attr, - &manager_attr_cpr_coef.attr, - NULL -}; - -static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct omap_overlay_manager *manager; - struct manager_attribute *manager_attr; - - manager = container_of(kobj, struct omap_overlay_manager, kobj); - manager_attr = container_of(attr, struct manager_attribute, attr); - - if (!manager_attr->show) - return -ENOENT; - - return manager_attr->show(manager, buf); -} - -static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t size) -{ - struct omap_overlay_manager *manager; - struct manager_attribute *manager_attr; - - manager = container_of(kobj, struct omap_overlay_manager, kobj); - manager_attr = container_of(attr, struct manager_attribute, attr); - - if (!manager_attr->store) - return -ENOENT; - - return manager_attr->store(manager, buf, size); -} - -static const struct sysfs_ops manager_sysfs_ops = { - .show = manager_attr_show, - .store = manager_attr_store, -}; - -static struct kobj_type manager_ktype = { - .sysfs_ops = &manager_sysfs_ops, - .default_attrs = manager_sysfs_attrs, -}; - -int dss_manager_kobj_init(struct omap_overlay_manager *mgr, - struct platform_device *pdev) -{ - return kobject_init_and_add(&mgr->kobj, &manager_ktype, - &pdev->dev.kobj, "manager%d", mgr->id); -} - -void dss_manager_kobj_uninit(struct omap_overlay_manager *mgr) -{ - kobject_del(&mgr->kobj); - kobject_put(&mgr->kobj); - - memset(&mgr->kobj, 0, sizeof(mgr->kobj)); -} diff --git a/drivers/gpu/drm/omapdrm/dss/manager.c b/drivers/gpu/drm/omapdrm/dss/manager.c deleted file mode 100644 index 08a67f4f6a20..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/manager.c +++ /dev/null @@ -1,263 +0,0 @@ -/* - * linux/drivers/video/omap2/dss/manager.c - * - * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> - * - * Some code and ideas taken from drivers/video/omap/ driver - * by Imre Deak. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#define DSS_SUBSYS_NAME "MANAGER" - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/jiffies.h> - -#include <video/omapdss.h> - -#include "dss.h" -#include "dss_features.h" - -static int num_managers; -static struct omap_overlay_manager *managers; - -int dss_init_overlay_managers(void) -{ - int i; - - num_managers = dss_feat_get_num_mgrs(); - - managers = kzalloc(sizeof(struct omap_overlay_manager) * num_managers, - GFP_KERNEL); - - BUG_ON(managers == NULL); - - for (i = 0; i < num_managers; ++i) { - struct omap_overlay_manager *mgr = &managers[i]; - - switch (i) { - case 0: - mgr->name = "lcd"; - mgr->id = OMAP_DSS_CHANNEL_LCD; - break; - case 1: - mgr->name = "tv"; - mgr->id = OMAP_DSS_CHANNEL_DIGIT; - break; - case 2: - mgr->name = "lcd2"; - mgr->id = OMAP_DSS_CHANNEL_LCD2; - break; - case 3: - mgr->name = "lcd3"; - mgr->id = OMAP_DSS_CHANNEL_LCD3; - break; - } - - mgr->caps = 0; - mgr->supported_displays = - dss_feat_get_supported_displays(mgr->id); - mgr->supported_outputs = - dss_feat_get_supported_outputs(mgr->id); - - INIT_LIST_HEAD(&mgr->overlays); - } - - return 0; -} - -int dss_init_overlay_managers_sysfs(struct platform_device *pdev) -{ - int i, r; - - for (i = 0; i < num_managers; ++i) { - struct omap_overlay_manager *mgr = &managers[i]; - - r = dss_manager_kobj_init(mgr, pdev); - if (r) - DSSERR("failed to create sysfs file\n"); - } - - return 0; -} - -void dss_uninit_overlay_managers(void) -{ - kfree(managers); - managers = NULL; - num_managers = 0; -} - -void dss_uninit_overlay_managers_sysfs(struct platform_device *pdev) -{ - int i; - - for (i = 0; i < num_managers; ++i) { - struct omap_overlay_manager *mgr = &managers[i]; - - dss_manager_kobj_uninit(mgr); - } -} - -int omap_dss_get_num_overlay_managers(void) -{ - return num_managers; -} -EXPORT_SYMBOL(omap_dss_get_num_overlay_managers); - -struct omap_overlay_manager *omap_dss_get_overlay_manager(int num) -{ - if (num >= num_managers) - return NULL; - - return &managers[num]; -} -EXPORT_SYMBOL(omap_dss_get_overlay_manager); - -int dss_mgr_simple_check(struct omap_overlay_manager *mgr, - const struct omap_overlay_manager_info *info) -{ - if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER)) { - /* - * OMAP3 supports only graphics source transparency color key - * and alpha blending simultaneously. See TRM 15.4.2.4.2.2 - * Alpha Mode. - */ - if (info->partial_alpha_enabled && info->trans_enabled - && info->trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) { - DSSERR("check_manager: illegal transparency key\n"); - return -EINVAL; - } - } - - return 0; -} - -static int dss_mgr_check_zorder(struct omap_overlay_manager *mgr, - struct omap_overlay_info **overlay_infos) -{ - struct omap_overlay *ovl1, *ovl2; - struct omap_overlay_info *info1, *info2; - - list_for_each_entry(ovl1, &mgr->overlays, list) { - info1 = overlay_infos[ovl1->id]; - - if (info1 == NULL) - continue; - - list_for_each_entry(ovl2, &mgr->overlays, list) { - if (ovl1 == ovl2) - continue; - - info2 = overlay_infos[ovl2->id]; - - if (info2 == NULL) - continue; - - if (info1->zorder == info2->zorder) { - DSSERR("overlays %d and %d have the same " - "zorder %d\n", - ovl1->id, ovl2->id, info1->zorder); - return -EINVAL; - } - } - } - - return 0; -} - -int dss_mgr_check_timings(struct omap_overlay_manager *mgr, - const struct omap_video_timings *timings) -{ - if (!dispc_mgr_timings_ok(mgr->id, timings)) { - DSSERR("check_manager: invalid timings\n"); - return -EINVAL; - } - - return 0; -} - -static int dss_mgr_check_lcd_config(struct omap_overlay_manager *mgr, - const struct dss_lcd_mgr_config *config) -{ - struct dispc_clock_info cinfo = config->clock_info; - int dl = config->video_port_width; - bool stallmode = config->stallmode; - bool fifohandcheck = config->fifohandcheck; - - if (cinfo.lck_div < 1 || cinfo.lck_div > 255) - return -EINVAL; - - if (cinfo.pck_div < 1 || cinfo.pck_div > 255) - return -EINVAL; - - if (dl != 12 && dl != 16 && dl != 18 && dl != 24) - return -EINVAL; - - /* fifohandcheck should be used only with stallmode */ - if (!stallmode && fifohandcheck) - return -EINVAL; - - /* - * io pad mode can be only checked by using dssdev connected to the - * manager. Ignore checking these for now, add checks when manager - * is capable of holding information related to the connected interface - */ - - return 0; -} - -int dss_mgr_check(struct omap_overlay_manager *mgr, - struct omap_overlay_manager_info *info, - const struct omap_video_timings *mgr_timings, - const struct dss_lcd_mgr_config *lcd_config, - struct omap_overlay_info **overlay_infos) -{ - struct omap_overlay *ovl; - int r; - - if (dss_has_feature(FEAT_ALPHA_FREE_ZORDER)) { - r = dss_mgr_check_zorder(mgr, overlay_infos); - if (r) - return r; - } - - r = dss_mgr_check_timings(mgr, mgr_timings); - if (r) - return r; - - r = dss_mgr_check_lcd_config(mgr, lcd_config); - if (r) - return r; - - list_for_each_entry(ovl, &mgr->overlays, list) { - struct omap_overlay_info *oi; - int r; - - oi = overlay_infos[ovl->id]; - - if (oi == NULL) - continue; - - r = dss_ovl_check(ovl, oi, mgr_timings); - if (r) - return r; - } - - return 0; -} diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h new file mode 100644 index 000000000000..d7e7c909bbc2 --- /dev/null +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2016 Texas Instruments + * Author: Tomi Valkeinen <tomi.valkeinen@ti.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. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __OMAP_DRM_DSS_H +#define __OMAP_DRM_DSS_H + +#include <video/omapdss.h> + +u32 dispc_read_irqstatus(void); +void dispc_clear_irqstatus(u32 mask); +u32 dispc_read_irqenable(void); +void dispc_write_irqenable(u32 mask); + +int dispc_request_irq(irq_handler_t handler, void *dev_id); +void dispc_free_irq(void *dev_id); + +int dispc_runtime_get(void); +void dispc_runtime_put(void); + +void dispc_mgr_enable(enum omap_channel channel, bool enable); +bool dispc_mgr_is_enabled(enum omap_channel channel); +u32 dispc_mgr_get_vsync_irq(enum omap_channel channel); +u32 dispc_mgr_get_framedone_irq(enum omap_channel channel); +u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel); +bool dispc_mgr_go_busy(enum omap_channel channel); +void dispc_mgr_go(enum omap_channel channel); +void dispc_mgr_set_lcd_config(enum omap_channel channel, + const struct dss_lcd_mgr_config *config); +void dispc_mgr_set_timings(enum omap_channel channel, + const struct omap_video_timings *timings); +void dispc_mgr_setup(enum omap_channel channel, + const struct omap_overlay_manager_info *info); + +int dispc_ovl_enable(enum omap_plane plane, bool enable); +bool dispc_ovl_enabled(enum omap_plane plane); +void dispc_ovl_set_channel_out(enum omap_plane plane, + enum omap_channel channel); +int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi, + bool replication, const struct omap_video_timings *mgr_timings, + bool mem_to_mem); + +enum omap_dss_output_id dispc_mgr_get_supported_outputs(enum omap_channel channel); + +struct dss_mgr_ops { + int (*connect)(enum omap_channel channel, + struct omap_dss_device *dst); + void (*disconnect)(enum omap_channel channel, + struct omap_dss_device *dst); + + void (*start_update)(enum omap_channel channel); + int (*enable)(enum omap_channel channel); + void (*disable)(enum omap_channel channel); + void (*set_timings)(enum omap_channel channel, + const struct omap_video_timings *timings); + void (*set_lcd_config)(enum omap_channel channel, + const struct dss_lcd_mgr_config *config); + int (*register_framedone_handler)(enum omap_channel channel, + void (*handler)(void *), void *data); + void (*unregister_framedone_handler)(enum omap_channel channel, + void (*handler)(void *), void *data); +}; + +int dss_install_mgr_ops(const struct dss_mgr_ops *mgr_ops); +void dss_uninstall_mgr_ops(void); + +int dss_mgr_connect(enum omap_channel channel, + struct omap_dss_device *dst); +void dss_mgr_disconnect(enum omap_channel channel, + struct omap_dss_device *dst); +void dss_mgr_set_timings(enum omap_channel channel, + const struct omap_video_timings *timings); +void dss_mgr_set_lcd_config(enum omap_channel channel, + const struct dss_lcd_mgr_config *config); +int dss_mgr_enable(enum omap_channel channel); +void dss_mgr_disable(enum omap_channel channel); +void dss_mgr_start_update(enum omap_channel channel); +int dss_mgr_register_framedone_handler(enum omap_channel channel, + void (*handler)(void *), void *data); +void dss_mgr_unregister_framedone_handler(enum omap_channel channel, + void (*handler)(void *), void *data); + +#endif /* __OMAP_DRM_DSS_H */ diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c index 16072159bd24..829232ad8c81 100644 --- a/drivers/gpu/drm/omapdrm/dss/output.c +++ b/drivers/gpu/drm/omapdrm/dss/output.c @@ -169,24 +169,6 @@ struct omap_dss_device *omapdss_find_output_from_display(struct omap_dss_device } EXPORT_SYMBOL(omapdss_find_output_from_display); -struct omap_overlay_manager *omapdss_find_mgr_from_display(struct omap_dss_device *dssdev) -{ - struct omap_dss_device *out; - struct omap_overlay_manager *mgr; - - out = omapdss_find_output_from_display(dssdev); - - if (out == NULL) - return NULL; - - mgr = out->manager; - - omap_dss_put_device(out); - - return mgr; -} -EXPORT_SYMBOL(omapdss_find_mgr_from_display); - static const struct dss_mgr_ops *dss_mgr_ops; int dss_install_mgr_ops(const struct dss_mgr_ops *mgr_ops) @@ -206,62 +188,62 @@ void dss_uninstall_mgr_ops(void) } EXPORT_SYMBOL(dss_uninstall_mgr_ops); -int dss_mgr_connect(struct omap_overlay_manager *mgr, +int dss_mgr_connect(enum omap_channel channel, struct omap_dss_device *dst) { - return dss_mgr_ops->connect(mgr, dst); + return dss_mgr_ops->connect(channel, dst); } EXPORT_SYMBOL(dss_mgr_connect); -void dss_mgr_disconnect(struct omap_overlay_manager *mgr, +void dss_mgr_disconnect(enum omap_channel channel, struct omap_dss_device *dst) { - dss_mgr_ops->disconnect(mgr, dst); + dss_mgr_ops->disconnect(channel, dst); } EXPORT_SYMBOL(dss_mgr_disconnect); -void dss_mgr_set_timings(struct omap_overlay_manager *mgr, +void dss_mgr_set_timings(enum omap_channel channel, const struct omap_video_timings *timings) { - dss_mgr_ops->set_timings(mgr, timings); + dss_mgr_ops->set_timings(channel, timings); } EXPORT_SYMBOL(dss_mgr_set_timings); -void dss_mgr_set_lcd_config(struct omap_overlay_manager *mgr, +void dss_mgr_set_lcd_config(enum omap_channel channel, const struct dss_lcd_mgr_config *config) { - dss_mgr_ops->set_lcd_config(mgr, config); + dss_mgr_ops->set_lcd_config(channel, config); } EXPORT_SYMBOL(dss_mgr_set_lcd_config); -int dss_mgr_enable(struct omap_overlay_manager *mgr) +int dss_mgr_enable(enum omap_channel channel) { - return dss_mgr_ops->enable(mgr); + return dss_mgr_ops->enable(channel); } EXPORT_SYMBOL(dss_mgr_enable); -void dss_mgr_disable(struct omap_overlay_manager *mgr) +void dss_mgr_disable(enum omap_channel channel) { - dss_mgr_ops->disable(mgr); + dss_mgr_ops->disable(channel); } EXPORT_SYMBOL(dss_mgr_disable); -void dss_mgr_start_update(struct omap_overlay_manager *mgr) +void dss_mgr_start_update(enum omap_channel channel) { - dss_mgr_ops->start_update(mgr); + dss_mgr_ops->start_update(channel); } EXPORT_SYMBOL(dss_mgr_start_update); -int dss_mgr_register_framedone_handler(struct omap_overlay_manager *mgr, +int dss_mgr_register_framedone_handler(enum omap_channel channel, void (*handler)(void *), void *data) { - return dss_mgr_ops->register_framedone_handler(mgr, handler, data); + return dss_mgr_ops->register_framedone_handler(channel, handler, data); } EXPORT_SYMBOL(dss_mgr_register_framedone_handler); -void dss_mgr_unregister_framedone_handler(struct omap_overlay_manager *mgr, +void dss_mgr_unregister_framedone_handler(enum omap_channel channel, void (*handler)(void *), void *data) { - dss_mgr_ops->unregister_framedone_handler(mgr, handler, data); + dss_mgr_ops->unregister_framedone_handler(channel, handler, data); } EXPORT_SYMBOL(dss_mgr_unregister_framedone_handler); diff --git a/drivers/gpu/drm/omapdrm/dss/overlay-sysfs.c b/drivers/gpu/drm/omapdrm/dss/overlay-sysfs.c deleted file mode 100644 index 4cc5ddebfb34..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/overlay-sysfs.c +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> - * - * Some code and ideas taken from drivers/video/omap/ driver - * by Imre Deak. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#define DSS_SUBSYS_NAME "OVERLAY" - -#include <linux/module.h> -#include <linux/err.h> -#include <linux/sysfs.h> -#include <linux/kobject.h> -#include <linux/platform_device.h> - -#include <video/omapdss.h> - -#include "dss.h" -#include "dss_features.h" - -static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name); -} - -static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%s\n", - ovl->manager ? ovl->manager->name : "<none>"); -} - -static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf, - size_t size) -{ - int i, r; - struct omap_overlay_manager *mgr = NULL; - struct omap_overlay_manager *old_mgr; - int len = size; - - if (buf[size-1] == '\n') - --len; - - if (len > 0) { - for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { - mgr = omap_dss_get_overlay_manager(i); - - if (sysfs_streq(buf, mgr->name)) - break; - - mgr = NULL; - } - } - - if (len > 0 && mgr == NULL) - return -EINVAL; - - if (mgr) - DSSDBG("manager %s found\n", mgr->name); - - if (mgr == ovl->manager) - return size; - - old_mgr = ovl->manager; - - r = dispc_runtime_get(); - if (r) - return r; - - /* detach old manager */ - if (old_mgr) { - r = ovl->unset_manager(ovl); - if (r) { - DSSERR("detach failed\n"); - goto err; - } - - r = old_mgr->apply(old_mgr); - if (r) - goto err; - } - - if (mgr) { - r = ovl->set_manager(ovl, mgr); - if (r) { - DSSERR("Failed to attach overlay\n"); - goto err; - } - - r = mgr->apply(mgr); - if (r) - goto err; - } - - dispc_runtime_put(); - - return size; - -err: - dispc_runtime_put(); - return r; -} - -static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf) -{ - struct omap_overlay_info info; - - ovl->get_overlay_info(ovl, &info); - - return snprintf(buf, PAGE_SIZE, "%d,%d\n", - info.width, info.height); -} - -static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf) -{ - struct omap_overlay_info info; - - ovl->get_overlay_info(ovl, &info); - - return snprintf(buf, PAGE_SIZE, "%d\n", info.screen_width); -} - -static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf) -{ - struct omap_overlay_info info; - - ovl->get_overlay_info(ovl, &info); - - return snprintf(buf, PAGE_SIZE, "%d,%d\n", - info.pos_x, info.pos_y); -} - -static ssize_t overlay_position_store(struct omap_overlay *ovl, - const char *buf, size_t size) -{ - int r; - char *last; - struct omap_overlay_info info; - - ovl->get_overlay_info(ovl, &info); - - info.pos_x = simple_strtoul(buf, &last, 10); - ++last; - if (last - buf >= size) - return -EINVAL; - - info.pos_y = simple_strtoul(last, &last, 10); - - r = ovl->set_overlay_info(ovl, &info); - if (r) - return r; - - if (ovl->manager) { - r = ovl->manager->apply(ovl->manager); - if (r) - return r; - } - - return size; -} - -static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf) -{ - struct omap_overlay_info info; - - ovl->get_overlay_info(ovl, &info); - - return snprintf(buf, PAGE_SIZE, "%d,%d\n", - info.out_width, info.out_height); -} - -static ssize_t overlay_output_size_store(struct omap_overlay *ovl, - const char *buf, size_t size) -{ - int r; - char *last; - struct omap_overlay_info info; - - ovl->get_overlay_info(ovl, &info); - - info.out_width = simple_strtoul(buf, &last, 10); - ++last; - if (last - buf >= size) - return -EINVAL; - - info.out_height = simple_strtoul(last, &last, 10); - - r = ovl->set_overlay_info(ovl, &info); - if (r) - return r; - - if (ovl->manager) { - r = ovl->manager->apply(ovl->manager); - if (r) - return r; - } - - return size; -} - -static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", ovl->is_enabled(ovl)); -} - -static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf, - size_t size) -{ - int r; - bool enable; - - r = strtobool(buf, &enable); - if (r) - return r; - - if (enable) - r = ovl->enable(ovl); - else - r = ovl->disable(ovl); - - if (r) - return r; - - return size; -} - -static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf) -{ - struct omap_overlay_info info; - - ovl->get_overlay_info(ovl, &info); - - return snprintf(buf, PAGE_SIZE, "%d\n", - info.global_alpha); -} - -static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, - const char *buf, size_t size) -{ - int r; - u8 alpha; - struct omap_overlay_info info; - - if ((ovl->caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0) - return -ENODEV; - - r = kstrtou8(buf, 0, &alpha); - if (r) - return r; - - ovl->get_overlay_info(ovl, &info); - - info.global_alpha = alpha; - - r = ovl->set_overlay_info(ovl, &info); - if (r) - return r; - - if (ovl->manager) { - r = ovl->manager->apply(ovl->manager); - if (r) - return r; - } - - return size; -} - -static ssize_t overlay_pre_mult_alpha_show(struct omap_overlay *ovl, - char *buf) -{ - struct omap_overlay_info info; - - ovl->get_overlay_info(ovl, &info); - - return snprintf(buf, PAGE_SIZE, "%d\n", - info.pre_mult_alpha); -} - -static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl, - const char *buf, size_t size) -{ - int r; - u8 alpha; - struct omap_overlay_info info; - - if ((ovl->caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0) - return -ENODEV; - - r = kstrtou8(buf, 0, &alpha); - if (r) - return r; - - ovl->get_overlay_info(ovl, &info); - - info.pre_mult_alpha = alpha; - - r = ovl->set_overlay_info(ovl, &info); - if (r) - return r; - - if (ovl->manager) { - r = ovl->manager->apply(ovl->manager); - if (r) - return r; - } - - return size; -} - -static ssize_t overlay_zorder_show(struct omap_overlay *ovl, char *buf) -{ - struct omap_overlay_info info; - - ovl->get_overlay_info(ovl, &info); - - return snprintf(buf, PAGE_SIZE, "%d\n", info.zorder); -} - -static ssize_t overlay_zorder_store(struct omap_overlay *ovl, - const char *buf, size_t size) -{ - int r; - u8 zorder; - struct omap_overlay_info info; - - if ((ovl->caps & OMAP_DSS_OVL_CAP_ZORDER) == 0) - return -ENODEV; - - r = kstrtou8(buf, 0, &zorder); - if (r) - return r; - - ovl->get_overlay_info(ovl, &info); - - info.zorder = zorder; - - r = ovl->set_overlay_info(ovl, &info); - if (r) - return r; - - if (ovl->manager) { - r = ovl->manager->apply(ovl->manager); - if (r) - return r; - } - - return size; -} - -struct overlay_attribute { - struct attribute attr; - ssize_t (*show)(struct omap_overlay *, char *); - ssize_t (*store)(struct omap_overlay *, const char *, size_t); -}; - -#define OVERLAY_ATTR(_name, _mode, _show, _store) \ - struct overlay_attribute overlay_attr_##_name = \ - __ATTR(_name, _mode, _show, _store) - -static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL); -static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR, - overlay_manager_show, overlay_manager_store); -static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL); -static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL); -static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR, - overlay_position_show, overlay_position_store); -static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR, - overlay_output_size_show, overlay_output_size_store); -static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR, - overlay_enabled_show, overlay_enabled_store); -static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR, - overlay_global_alpha_show, overlay_global_alpha_store); -static OVERLAY_ATTR(pre_mult_alpha, S_IRUGO|S_IWUSR, - overlay_pre_mult_alpha_show, - overlay_pre_mult_alpha_store); -static OVERLAY_ATTR(zorder, S_IRUGO|S_IWUSR, - overlay_zorder_show, overlay_zorder_store); - -static struct attribute *overlay_sysfs_attrs[] = { - &overlay_attr_name.attr, - &overlay_attr_manager.attr, - &overlay_attr_input_size.attr, - &overlay_attr_screen_width.attr, - &overlay_attr_position.attr, - &overlay_attr_output_size.attr, - &overlay_attr_enabled.attr, - &overlay_attr_global_alpha.attr, - &overlay_attr_pre_mult_alpha.attr, - &overlay_attr_zorder.attr, - NULL -}; - -static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct omap_overlay *overlay; - struct overlay_attribute *overlay_attr; - - overlay = container_of(kobj, struct omap_overlay, kobj); - overlay_attr = container_of(attr, struct overlay_attribute, attr); - - if (!overlay_attr->show) - return -ENOENT; - - return overlay_attr->show(overlay, buf); -} - -static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t size) -{ - struct omap_overlay *overlay; - struct overlay_attribute *overlay_attr; - - overlay = container_of(kobj, struct omap_overlay, kobj); - overlay_attr = container_of(attr, struct overlay_attribute, attr); - - if (!overlay_attr->store) - return -ENOENT; - - return overlay_attr->store(overlay, buf, size); -} - -static const struct sysfs_ops overlay_sysfs_ops = { - .show = overlay_attr_show, - .store = overlay_attr_store, -}; - -static struct kobj_type overlay_ktype = { - .sysfs_ops = &overlay_sysfs_ops, - .default_attrs = overlay_sysfs_attrs, -}; - -int dss_overlay_kobj_init(struct omap_overlay *ovl, - struct platform_device *pdev) -{ - return kobject_init_and_add(&ovl->kobj, &overlay_ktype, - &pdev->dev.kobj, "overlay%d", ovl->id); -} - -void dss_overlay_kobj_uninit(struct omap_overlay *ovl) -{ - kobject_del(&ovl->kobj); - kobject_put(&ovl->kobj); -} diff --git a/drivers/gpu/drm/omapdrm/dss/overlay.c b/drivers/gpu/drm/omapdrm/dss/overlay.c deleted file mode 100644 index 2f7cee985cdd..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/overlay.c +++ /dev/null @@ -1,202 +0,0 @@ -/* - * linux/drivers/video/omap2/dss/overlay.c - * - * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> - * - * Some code and ideas taken from drivers/video/omap/ driver - * by Imre Deak. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#define DSS_SUBSYS_NAME "OVERLAY" - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/err.h> -#include <linux/sysfs.h> -#include <linux/platform_device.h> -#include <linux/delay.h> -#include <linux/slab.h> - -#include <video/omapdss.h> - -#include "dss.h" -#include "dss_features.h" - -static int num_overlays; -static struct omap_overlay *overlays; - -int omap_dss_get_num_overlays(void) -{ - return num_overlays; -} -EXPORT_SYMBOL(omap_dss_get_num_overlays); - -struct omap_overlay *omap_dss_get_overlay(int num) -{ - if (num >= num_overlays) - return NULL; - - return &overlays[num]; -} -EXPORT_SYMBOL(omap_dss_get_overlay); - -void dss_init_overlays(struct platform_device *pdev) -{ - int i, r; - - num_overlays = dss_feat_get_num_ovls(); - - overlays = kzalloc(sizeof(struct omap_overlay) * num_overlays, - GFP_KERNEL); - - BUG_ON(overlays == NULL); - - for (i = 0; i < num_overlays; ++i) { - struct omap_overlay *ovl = &overlays[i]; - - switch (i) { - case 0: - ovl->name = "gfx"; - ovl->id = OMAP_DSS_GFX; - break; - case 1: - ovl->name = "vid1"; - ovl->id = OMAP_DSS_VIDEO1; - break; - case 2: - ovl->name = "vid2"; - ovl->id = OMAP_DSS_VIDEO2; - break; - case 3: - ovl->name = "vid3"; - ovl->id = OMAP_DSS_VIDEO3; - break; - } - - ovl->caps = dss_feat_get_overlay_caps(ovl->id); - ovl->supported_modes = - dss_feat_get_supported_color_modes(ovl->id); - - r = dss_overlay_kobj_init(ovl, pdev); - if (r) - DSSERR("failed to create sysfs file\n"); - } -} - -void dss_uninit_overlays(struct platform_device *pdev) -{ - int i; - - for (i = 0; i < num_overlays; ++i) { - struct omap_overlay *ovl = &overlays[i]; - dss_overlay_kobj_uninit(ovl); - } - - kfree(overlays); - overlays = NULL; - num_overlays = 0; -} - -int dss_ovl_simple_check(struct omap_overlay *ovl, - const struct omap_overlay_info *info) -{ - if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { - if (info->out_width != 0 && info->width != info->out_width) { - DSSERR("check_overlay: overlay %d doesn't support " - "scaling\n", ovl->id); - return -EINVAL; - } - - if (info->out_height != 0 && info->height != info->out_height) { - DSSERR("check_overlay: overlay %d doesn't support " - "scaling\n", ovl->id); - return -EINVAL; - } - } - - if ((ovl->supported_modes & info->color_mode) == 0) { - DSSERR("check_overlay: overlay %d doesn't support mode %d\n", - ovl->id, info->color_mode); - return -EINVAL; - } - - if (info->zorder >= omap_dss_get_num_overlays()) { - DSSERR("check_overlay: zorder %d too high\n", info->zorder); - return -EINVAL; - } - - if (dss_feat_rotation_type_supported(info->rotation_type) == 0) { - DSSERR("check_overlay: rotation type %d not supported\n", - info->rotation_type); - return -EINVAL; - } - - return 0; -} - -int dss_ovl_check(struct omap_overlay *ovl, struct omap_overlay_info *info, - const struct omap_video_timings *mgr_timings) -{ - u16 outw, outh; - u16 dw, dh; - - dw = mgr_timings->x_res; - dh = mgr_timings->y_res; - - if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { - outw = info->width; - outh = info->height; - } else { - if (info->out_width == 0) - outw = info->width; - else - outw = info->out_width; - - if (info->out_height == 0) - outh = info->height; - else - outh = info->out_height; - } - - if (dw < info->pos_x + outw) { - DSSERR("overlay %d horizontally not inside the display area " - "(%d + %d >= %d)\n", - ovl->id, info->pos_x, outw, dw); - return -EINVAL; - } - - if (dh < info->pos_y + outh) { - DSSERR("overlay %d vertically not inside the display area " - "(%d + %d >= %d)\n", - ovl->id, info->pos_y, outh, dh); - return -EINVAL; - } - - return 0; -} - -/* - * Checks if replication logic should be used. Only use when overlay is in - * RGB12U or RGB16 mode, and video port width interface is 18bpp or 24bpp - */ -bool dss_ovl_use_replication(struct dss_lcd_mgr_config config, - enum omap_color_mode mode) -{ - if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16) - return false; - - return config.video_port_width > 16; -} diff --git a/drivers/gpu/drm/omapdrm/dss/rfbi.c b/drivers/gpu/drm/omapdrm/dss/rfbi.c index aea6a1d0fb20..3796576dfadf 100644 --- a/drivers/gpu/drm/omapdrm/dss/rfbi.c +++ b/drivers/gpu/drm/omapdrm/dss/rfbi.c @@ -880,7 +880,7 @@ static int rfbi_display_enable(struct omap_dss_device *dssdev) struct omap_dss_device *out = &rfbi.output; int r; - if (out->manager == NULL) { + if (!out->dispc_channel_connected) { DSSERR("failed to enable display: no output/manager\n"); return -ENODEV; } diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c b/drivers/gpu/drm/omapdrm/dss/sdi.c index d747cc6b59e1..cd6d3bfb041d 100644 --- a/drivers/gpu/drm/omapdrm/dss/sdi.c +++ b/drivers/gpu/drm/omapdrm/dss/sdi.c @@ -114,7 +114,7 @@ static int sdi_calc_clock_div(unsigned long pclk, static void sdi_config_lcd_manager(struct omap_dss_device *dssdev) { - struct omap_overlay_manager *mgr = sdi.output.manager; + enum omap_channel channel = dssdev->dispc_channel; sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; @@ -124,19 +124,20 @@ static void sdi_config_lcd_manager(struct omap_dss_device *dssdev) sdi.mgr_config.video_port_width = 24; sdi.mgr_config.lcden_sig_polarity = 1; - dss_mgr_set_lcd_config(mgr, &sdi.mgr_config); + dss_mgr_set_lcd_config(channel, &sdi.mgr_config); } static int sdi_display_enable(struct omap_dss_device *dssdev) { struct omap_dss_device *out = &sdi.output; + enum omap_channel channel = dssdev->dispc_channel; struct omap_video_timings *t = &sdi.timings; unsigned long fck; struct dispc_clock_info dispc_cinfo; unsigned long pck; int r; - if (out->manager == NULL) { + if (!out->dispc_channel_connected) { DSSERR("failed to enable display: no output/manager\n"); return -ENODEV; } @@ -169,7 +170,7 @@ static int sdi_display_enable(struct omap_dss_device *dssdev) } - dss_mgr_set_timings(out->manager, t); + dss_mgr_set_timings(channel, t); r = dss_set_fck_rate(fck); if (r) @@ -188,7 +189,7 @@ static int sdi_display_enable(struct omap_dss_device *dssdev) * need to care about the shadow register mechanism for pck-free. The * exact reason for this is unknown. */ - dispc_mgr_set_clock_div(out->manager->id, &sdi.mgr_config.clock_info); + dispc_mgr_set_clock_div(channel, &sdi.mgr_config.clock_info); dss_sdi_init(sdi.datapairs); r = dss_sdi_enable(); @@ -196,7 +197,7 @@ static int sdi_display_enable(struct omap_dss_device *dssdev) goto err_sdi_enable; mdelay(2); - r = dss_mgr_enable(out->manager); + r = dss_mgr_enable(channel); if (r) goto err_mgr_enable; @@ -216,9 +217,9 @@ err_reg_enable: static void sdi_display_disable(struct omap_dss_device *dssdev) { - struct omap_overlay_manager *mgr = sdi.output.manager; + enum omap_channel channel = dssdev->dispc_channel; - dss_mgr_disable(mgr); + dss_mgr_disable(channel); dss_sdi_disable(); @@ -242,9 +243,9 @@ static void sdi_get_timings(struct omap_dss_device *dssdev, static int sdi_check_timings(struct omap_dss_device *dssdev, struct omap_video_timings *timings) { - struct omap_overlay_manager *mgr = sdi.output.manager; + enum omap_channel channel = dssdev->dispc_channel; - if (mgr && !dispc_mgr_timings_ok(mgr->id, timings)) + if (!dispc_mgr_timings_ok(channel, timings)) return -EINVAL; if (timings->pixelclock == 0) @@ -280,18 +281,14 @@ static int sdi_init_regulator(void) static int sdi_connect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { - struct omap_overlay_manager *mgr; + enum omap_channel channel = dssdev->dispc_channel; int r; r = sdi_init_regulator(); if (r) return r; - mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); - if (!mgr) - return -ENODEV; - - r = dss_mgr_connect(mgr, dssdev); + r = dss_mgr_connect(channel, dssdev); if (r) return r; @@ -299,7 +296,7 @@ static int sdi_connect(struct omap_dss_device *dssdev, if (r) { DSSERR("failed to connect output to new device: %s\n", dst->name); - dss_mgr_disconnect(mgr, dssdev); + dss_mgr_disconnect(channel, dssdev); return r; } @@ -309,6 +306,8 @@ static int sdi_connect(struct omap_dss_device *dssdev, static void sdi_disconnect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { + enum omap_channel channel = dssdev->dispc_channel; + WARN_ON(dst != dssdev->dst); if (dst != dssdev->dst) @@ -316,8 +315,7 @@ static void sdi_disconnect(struct omap_dss_device *dssdev, omapdss_output_unset_device(dssdev); - if (dssdev->manager) - dss_mgr_disconnect(dssdev->manager, dssdev); + dss_mgr_disconnect(channel, dssdev); } static const struct omapdss_sdi_ops sdi_ops = { diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index 08f9def76e27..08a2cc778ba9 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -443,7 +443,7 @@ static const struct venc_config *venc_timings_to_config( static int venc_power_on(struct omap_dss_device *dssdev) { - struct omap_overlay_manager *mgr = venc.output.manager; + enum omap_channel channel = dssdev->dispc_channel; u32 l; int r; @@ -469,13 +469,13 @@ static int venc_power_on(struct omap_dss_device *dssdev) venc_write_reg(VENC_OUTPUT_CONTROL, l); - dss_mgr_set_timings(mgr, &venc.timings); + dss_mgr_set_timings(channel, &venc.timings); r = regulator_enable(venc.vdda_dac_reg); if (r) goto err1; - r = dss_mgr_enable(mgr); + r = dss_mgr_enable(channel); if (r) goto err2; @@ -494,12 +494,12 @@ err0: static void venc_power_off(struct omap_dss_device *dssdev) { - struct omap_overlay_manager *mgr = venc.output.manager; + enum omap_channel channel = dssdev->dispc_channel; venc_write_reg(VENC_OUTPUT_CONTROL, 0); dss_set_dac_pwrdn_bgz(0); - dss_mgr_disable(mgr); + dss_mgr_disable(channel); regulator_disable(venc.vdda_dac_reg); @@ -515,7 +515,7 @@ static int venc_display_enable(struct omap_dss_device *dssdev) mutex_lock(&venc.venc_lock); - if (out->manager == NULL) { + if (!out->dispc_channel_connected) { DSSERR("Failed to enable display: no output/manager\n"); r = -ENODEV; goto err0; @@ -742,18 +742,14 @@ static int venc_get_clocks(struct platform_device *pdev) static int venc_connect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { - struct omap_overlay_manager *mgr; + enum omap_channel channel = dssdev->dispc_channel; int r; r = venc_init_regulator(); if (r) return r; - mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); - if (!mgr) - return -ENODEV; - - r = dss_mgr_connect(mgr, dssdev); + r = dss_mgr_connect(channel, dssdev); if (r) return r; @@ -761,7 +757,7 @@ static int venc_connect(struct omap_dss_device *dssdev, if (r) { DSSERR("failed to connect output to new device: %s\n", dst->name); - dss_mgr_disconnect(mgr, dssdev); + dss_mgr_disconnect(channel, dssdev); return r; } @@ -771,6 +767,8 @@ static int venc_connect(struct omap_dss_device *dssdev, static void venc_disconnect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { + enum omap_channel channel = dssdev->dispc_channel; + WARN_ON(dst != dssdev->dst); if (dst != dssdev->dst) @@ -778,8 +776,7 @@ static void venc_disconnect(struct omap_dss_device *dssdev, omapdss_output_unset_device(dssdev); - if (dssdev->manager) - dss_mgr_disconnect(dssdev->manager, dssdev); + dss_mgr_disconnect(channel, dssdev); } static const struct omapdss_atv_ops venc_ops = { diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c index 83f2a9177c14..ce2d67b6a8c7 100644 --- a/drivers/gpu/drm/omapdrm/omap_connector.c +++ b/drivers/gpu/drm/omapdrm/omap_connector.c @@ -63,6 +63,9 @@ void copy_timings_omap_to_drm(struct drm_display_mode *mode, if (timings->interlace) mode->flags |= DRM_MODE_FLAG_INTERLACE; + if (timings->double_pixel) + mode->flags |= DRM_MODE_FLAG_DBLCLK; + if (timings->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH) mode->flags |= DRM_MODE_FLAG_PHSYNC; else @@ -90,6 +93,7 @@ void copy_timings_drm_to_omap(struct omap_video_timings *timings, timings->vbp = mode->vtotal - mode->vsync_end; timings->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); + timings->double_pixel = !!(mode->flags & DRM_MODE_FLAG_DBLCLK); if (mode->flags & DRM_MODE_FLAG_PHSYNC) timings->hsync_level = OMAPDSS_SIG_ACTIVE_HIGH; diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index d38fcbcc43a8..04097dab8589 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -34,14 +34,6 @@ struct omap_crtc { const char *name; enum omap_channel channel; - /* - * Temporary: eventually this will go away, but it is needed - * for now to keep the output's happy. (They only need - * mgr->id.) Eventually this will be replaced w/ something - * more common-panel-framework-y - */ - struct omap_overlay_manager *mgr; - struct omap_video_timings timings; struct omap_drm_irq vblank_irq; @@ -80,9 +72,13 @@ int omap_crtc_wait_pending(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + /* + * Timeout is set to a "sufficiently" high value, which should cover + * a single frame refresh even on slower displays. + */ return wait_event_timeout(omap_crtc->pending_wait, !omap_crtc->pending, - msecs_to_jiffies(50)); + msecs_to_jiffies(250)); } /* ----------------------------------------------------------------------------- @@ -100,31 +96,32 @@ int omap_crtc_wait_pending(struct drm_crtc *crtc) /* ovl-mgr-id -> crtc */ static struct omap_crtc *omap_crtcs[8]; +static struct omap_dss_device *omap_crtc_output[8]; /* we can probably ignore these until we support command-mode panels: */ -static int omap_crtc_dss_connect(struct omap_overlay_manager *mgr, +static int omap_crtc_dss_connect(enum omap_channel channel, struct omap_dss_device *dst) { - if (mgr->output) + if (omap_crtc_output[channel]) return -EINVAL; - if ((mgr->supported_outputs & dst->id) == 0) + if ((dispc_mgr_get_supported_outputs(channel) & dst->id) == 0) return -EINVAL; - dst->manager = mgr; - mgr->output = dst; + omap_crtc_output[channel] = dst; + dst->dispc_channel_connected = true; return 0; } -static void omap_crtc_dss_disconnect(struct omap_overlay_manager *mgr, +static void omap_crtc_dss_disconnect(enum omap_channel channel, struct omap_dss_device *dst) { - mgr->output->manager = NULL; - mgr->output = NULL; + omap_crtc_output[channel] = NULL; + dst->dispc_channel_connected = false; } -static void omap_crtc_dss_start_update(struct omap_overlay_manager *mgr) +static void omap_crtc_dss_start_update(enum omap_channel channel) { } @@ -138,6 +135,11 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) u32 framedone_irq, vsync_irq; int ret; + if (omap_crtc_output[channel]->output_type == OMAP_DISPLAY_TYPE_HDMI) { + dispc_mgr_enable(channel, enable); + return; + } + if (dispc_mgr_is_enabled(channel) == enable) return; @@ -186,9 +188,9 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) } -static int omap_crtc_dss_enable(struct omap_overlay_manager *mgr) +static int omap_crtc_dss_enable(enum omap_channel channel) { - struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; + struct omap_crtc *omap_crtc = omap_crtcs[channel]; struct omap_overlay_manager_info info; memset(&info, 0, sizeof(info)); @@ -205,38 +207,38 @@ static int omap_crtc_dss_enable(struct omap_overlay_manager *mgr) return 0; } -static void omap_crtc_dss_disable(struct omap_overlay_manager *mgr) +static void omap_crtc_dss_disable(enum omap_channel channel) { - struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; + struct omap_crtc *omap_crtc = omap_crtcs[channel]; omap_crtc_set_enabled(&omap_crtc->base, false); } -static void omap_crtc_dss_set_timings(struct omap_overlay_manager *mgr, +static void omap_crtc_dss_set_timings(enum omap_channel channel, const struct omap_video_timings *timings) { - struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; + struct omap_crtc *omap_crtc = omap_crtcs[channel]; DBG("%s", omap_crtc->name); omap_crtc->timings = *timings; } -static void omap_crtc_dss_set_lcd_config(struct omap_overlay_manager *mgr, +static void omap_crtc_dss_set_lcd_config(enum omap_channel channel, const struct dss_lcd_mgr_config *config) { - struct omap_crtc *omap_crtc = omap_crtcs[mgr->id]; + struct omap_crtc *omap_crtc = omap_crtcs[channel]; DBG("%s", omap_crtc->name); dispc_mgr_set_lcd_config(omap_crtc->channel, config); } static int omap_crtc_dss_register_framedone( - struct omap_overlay_manager *mgr, + enum omap_channel channel, void (*handler)(void *), void *data) { return 0; } static void omap_crtc_dss_unregister_framedone( - struct omap_overlay_manager *mgr, + enum omap_channel channel, void (*handler)(void *), void *data) { } @@ -403,24 +405,40 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc, } } +static bool omap_crtc_is_plane_prop(struct drm_device *dev, + struct drm_property *property) +{ + struct omap_drm_private *priv = dev->dev_private; + + return property == priv->zorder_prop || + property == dev->mode_config.rotation_property; +} + static int omap_crtc_atomic_set_property(struct drm_crtc *crtc, struct drm_crtc_state *state, struct drm_property *property, uint64_t val) { - struct drm_plane_state *plane_state; - struct drm_plane *plane = crtc->primary; + struct drm_device *dev = crtc->dev; - /* - * Delegate property set to the primary plane. Get the plane state and - * set the property directly. - */ + if (omap_crtc_is_plane_prop(dev, property)) { + struct drm_plane_state *plane_state; + struct drm_plane *plane = crtc->primary; - plane_state = drm_atomic_get_plane_state(state->state, plane); - if (!plane_state) - return -EINVAL; + /* + * Delegate property set to the primary plane. Get the plane + * state and set the property directly. + */ + + plane_state = drm_atomic_get_plane_state(state->state, plane); + if (IS_ERR(plane_state)) + return PTR_ERR(plane_state); - return drm_atomic_plane_set_property(plane, plane_state, property, val); + return drm_atomic_plane_set_property(plane, plane_state, + property, val); + } + + return -EINVAL; } static int omap_crtc_atomic_get_property(struct drm_crtc *crtc, @@ -428,14 +446,20 @@ static int omap_crtc_atomic_get_property(struct drm_crtc *crtc, struct drm_property *property, uint64_t *val) { - /* - * Delegate property get to the primary plane. The - * drm_atomic_plane_get_property() function isn't exported, but can be - * called through drm_object_property_get_value() as that will call - * drm_atomic_get_property() for atomic drivers. - */ - return drm_object_property_get_value(&crtc->primary->base, property, - val); + struct drm_device *dev = crtc->dev; + + if (omap_crtc_is_plane_prop(dev, property)) { + /* + * Delegate property get to the primary plane. The + * drm_atomic_plane_get_property() function isn't exported, but + * can be called through drm_object_property_get_value() as that + * will call drm_atomic_get_property() for atomic drivers. + */ + return drm_object_property_get_value(&crtc->primary->base, + property, val); + } + + return -EINVAL; } static const struct drm_crtc_funcs omap_crtc_funcs = { @@ -509,9 +533,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, omap_crtc->error_irq.irq = omap_crtc_error_irq; omap_irq_register(dev, &omap_crtc->error_irq); - /* temporary: */ - omap_crtc->mgr = omap_dss_get_overlay_manager(channel); - ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, &omap_crtc_funcs, NULL); if (ret < 0) { diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c index dfebdc4aa0f2..9f94576c435d 100644 --- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c @@ -79,6 +79,16 @@ static const uint32_t reg[][4] = { DMM_PAT_DESCR__2, DMM_PAT_DESCR__3}, }; +static u32 dmm_read(struct dmm *dmm, u32 reg) +{ + return readl(dmm->base + reg); +} + +static void dmm_write(struct dmm *dmm, u32 val, u32 reg) +{ + writel(val, dmm->base + reg); +} + /* simple allocator to grab next 16 byte aligned memory from txn */ static void *alloc_dma(struct dmm_txn *txn, size_t sz, dma_addr_t *pa) { @@ -108,7 +118,7 @@ static int wait_status(struct refill_engine *engine, uint32_t wait_mask) i = DMM_FIXED_RETRY_COUNT; while (true) { - r = readl(dmm->base + reg[PAT_STATUS][engine->id]); + r = dmm_read(dmm, reg[PAT_STATUS][engine->id]); err = r & DMM_PATSTATUS_ERR; if (err) return -EFAULT; @@ -140,11 +150,11 @@ static void release_engine(struct refill_engine *engine) static irqreturn_t omap_dmm_irq_handler(int irq, void *arg) { struct dmm *dmm = arg; - uint32_t status = readl(dmm->base + DMM_PAT_IRQSTATUS); + uint32_t status = dmm_read(dmm, DMM_PAT_IRQSTATUS); int i; /* ack IRQ */ - writel(status, dmm->base + DMM_PAT_IRQSTATUS); + dmm_write(dmm, status, DMM_PAT_IRQSTATUS); for (i = 0; i < dmm->num_engines; i++) { if (status & DMM_IRQSTAT_LST) { @@ -264,7 +274,7 @@ static int dmm_txn_commit(struct dmm_txn *txn, bool wait) txn->last_pat->next_pa = 0; /* write to PAT_DESCR to clear out any pending transaction */ - writel(0x0, dmm->base + reg[PAT_DESCR][engine->id]); + dmm_write(dmm, 0x0, reg[PAT_DESCR][engine->id]); /* wait for engine ready: */ ret = wait_status(engine, DMM_PATSTATUS_READY); @@ -280,8 +290,7 @@ static int dmm_txn_commit(struct dmm_txn *txn, bool wait) smp_mb(); /* kick reload */ - writel(engine->refill_pa, - dmm->base + reg[PAT_DESCR][engine->id]); + dmm_write(dmm, engine->refill_pa, reg[PAT_DESCR][engine->id]); if (wait) { if (!wait_for_completion_timeout(&engine->compl, @@ -309,6 +318,21 @@ static int fill(struct tcm_area *area, struct page **pages, struct tcm_area slice, area_s; struct dmm_txn *txn; + /* + * FIXME + * + * Asynchronous fill does not work reliably, as the driver does not + * handle errors in the async code paths. The fill operation may + * silently fail, leading to leaking DMM engines, which may eventually + * lead to deadlock if we run out of DMM engines. + * + * For now, always set 'wait' so that we only use sync fills. Async + * fills should be fixed, or alternatively we could decide to only + * support sync fills and so the whole async code path could be removed. + */ + + wait = true; + txn = dmm_txn_init(omap_dmm, area->tcm); if (IS_ERR_OR_NULL(txn)) return -ENOMEM; @@ -642,7 +666,7 @@ static int omap_dmm_probe(struct platform_device *dev) omap_dmm->dev = &dev->dev; - hwinfo = readl(omap_dmm->base + DMM_PAT_HWINFO); + hwinfo = dmm_read(omap_dmm, DMM_PAT_HWINFO); omap_dmm->num_engines = (hwinfo >> 24) & 0x1F; omap_dmm->num_lut = (hwinfo >> 16) & 0x1F; omap_dmm->container_width = 256; @@ -651,7 +675,7 @@ static int omap_dmm_probe(struct platform_device *dev) atomic_set(&omap_dmm->engine_counter, omap_dmm->num_engines); /* read out actual LUT width and height */ - pat_geom = readl(omap_dmm->base + DMM_PAT_GEOMETRY); + pat_geom = dmm_read(omap_dmm, DMM_PAT_GEOMETRY); omap_dmm->lut_width = ((pat_geom >> 16) & 0xF) << 5; omap_dmm->lut_height = ((pat_geom >> 24) & 0xF) << 5; @@ -661,12 +685,12 @@ static int omap_dmm_probe(struct platform_device *dev) omap_dmm->num_lut++; /* initialize DMM registers */ - writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__0); - writel(0x88888888, omap_dmm->base + DMM_PAT_VIEW__1); - writel(0x80808080, omap_dmm->base + DMM_PAT_VIEW_MAP__0); - writel(0x80000000, omap_dmm->base + DMM_PAT_VIEW_MAP_BASE); - writel(0x88888888, omap_dmm->base + DMM_TILER_OR__0); - writel(0x88888888, omap_dmm->base + DMM_TILER_OR__1); + dmm_write(omap_dmm, 0x88888888, DMM_PAT_VIEW__0); + dmm_write(omap_dmm, 0x88888888, DMM_PAT_VIEW__1); + dmm_write(omap_dmm, 0x80808080, DMM_PAT_VIEW_MAP__0); + dmm_write(omap_dmm, 0x80000000, DMM_PAT_VIEW_MAP_BASE); + dmm_write(omap_dmm, 0x88888888, DMM_TILER_OR__0); + dmm_write(omap_dmm, 0x88888888, DMM_TILER_OR__1); ret = request_irq(omap_dmm->irq, omap_dmm_irq_handler, IRQF_SHARED, "omap_dmm_irq_handler", omap_dmm); @@ -684,7 +708,7 @@ static int omap_dmm_probe(struct platform_device *dev) * buffers for accelerated pan/scroll) and FILL_DSC<n> which * we just generally don't care about. */ - writel(0x7e7e7e7e, omap_dmm->base + DMM_PAT_IRQENABLE_SET); + dmm_write(omap_dmm, 0x7e7e7e7e, DMM_PAT_IRQENABLE_SET); omap_dmm->dummy_page = alloc_page(GFP_KERNEL | __GFP_DMA32); if (!omap_dmm->dummy_page) { diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 33370f42e4d7..80398a684cae 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -340,7 +340,7 @@ static int omap_modeset_init(struct drm_device *dev) struct drm_connector *connector; struct drm_encoder *encoder; enum omap_channel channel; - struct omap_overlay_manager *mgr; + struct omap_dss_device *out; if (!omapdss_device_is_connected(dssdev)) continue; @@ -387,8 +387,10 @@ static int omap_modeset_init(struct drm_device *dev) * not considered. */ - mgr = omapdss_find_mgr_from_display(dssdev); - channel = mgr->id; + out = omapdss_find_output_from_display(dssdev); + channel = out->dispc_channel; + omap_dss_put_device(out); + /* * if this channel hasn't already been taken by a previously * allocated crtc, we create a new crtc for it @@ -858,12 +860,52 @@ static int pdev_remove(struct platform_device *device) } #ifdef CONFIG_PM_SLEEP +static int omap_drm_suspend_all_displays(void) +{ + struct omap_dss_device *dssdev = NULL; + + for_each_dss_dev(dssdev) { + if (!dssdev->driver) + continue; + + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { + dssdev->driver->disable(dssdev); + dssdev->activate_after_resume = true; + } else { + dssdev->activate_after_resume = false; + } + } + + return 0; +} + +static int omap_drm_resume_all_displays(void) +{ + struct omap_dss_device *dssdev = NULL; + + for_each_dss_dev(dssdev) { + if (!dssdev->driver) + continue; + + if (dssdev->activate_after_resume) { + dssdev->driver->enable(dssdev); + dssdev->activate_after_resume = false; + } + } + + return 0; +} + static int omap_drm_suspend(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); drm_kms_helper_poll_disable(drm_dev); + drm_modeset_lock_all(drm_dev); + omap_drm_suspend_all_displays(); + drm_modeset_unlock_all(drm_dev); + return 0; } @@ -871,6 +913,10 @@ static int omap_drm_resume(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); + drm_modeset_lock_all(drm_dev); + omap_drm_resume_all_displays(); + drm_modeset_unlock_all(drm_dev); + drm_kms_helper_poll_enable(drm_dev); return omap_gem_resume(dev); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index c23cbe6fe9e4..0fbe17d0ec6f 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -31,6 +31,8 @@ #include <drm/drm_gem.h> #include <drm/omap_drm.h> +#include "dss/omapdss.h" + #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) #define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */ @@ -188,12 +190,15 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, struct omap_drm_window *win, struct omap_overlay_info *info); struct drm_connector *omap_framebuffer_get_next_connector( struct drm_framebuffer *fb, struct drm_connector *from); +bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb); void omap_gem_init(struct drm_device *dev); void omap_gem_deinit(struct drm_device *dev); struct drm_gem_object *omap_gem_new(struct drm_device *dev, union omap_gem_size gsize, uint32_t flags); +struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size, + struct sg_table *sgt); int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file, union omap_gem_size gsize, uint32_t flags, uint32_t *handle); void omap_gem_free_object(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/omapdrm/omap_encoder.c b/drivers/gpu/drm/omapdrm/omap_encoder.c index 61714e9670ae..0bbb9c59622e 100644 --- a/drivers/gpu/drm/omapdrm/omap_encoder.c +++ b/drivers/gpu/drm/omapdrm/omap_encoder.c @@ -139,11 +139,16 @@ static void omap_encoder_enable(struct drm_encoder *encoder) struct omap_encoder *omap_encoder = to_omap_encoder(encoder); struct omap_dss_device *dssdev = omap_encoder->dssdev; struct omap_dss_driver *dssdrv = dssdev->driver; + int r; omap_encoder_update(encoder, omap_crtc_channel(encoder->crtc), omap_crtc_timings(encoder->crtc)); - dssdrv->enable(dssdev); + r = dssdrv->enable(dssdev); + if (r) + dev_err(encoder->dev->dev, + "Failed to enable display '%s': %d\n", + dssdev->name, r); } static int omap_encoder_atomic_check(struct drm_encoder *encoder, diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index ad202dfc1a49..610962396eb0 100644 --- a/drivers/gpu/drm/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -145,6 +145,14 @@ static uint32_t get_linear_addr(struct plane *plane, return plane->paddr + offset; } +bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb) +{ + struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); + struct plane *plane = &omap_fb->planes[0]; + + return omap_gem_flags(plane->bo) & OMAP_BO_TILED; +} + /* update ovl info for scanout, handles cases of multi-planar fb's, etc. */ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, @@ -449,6 +457,14 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, goto fail; } + if (i > 0 && pitch != mode_cmd->pitches[i - 1]) { + dev_err(dev->dev, + "pitches are not the same between framebuffer planes %d != %d\n", + pitch, mode_cmd->pitches[i - 1]); + ret = -EINVAL; + goto fail; + } + plane->bo = bos[i]; plane->offset = mode_cmd->offsets[i]; plane->pitch = pitch; diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 8495a1a4b617..cc36a8dc9bd4 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -31,9 +31,9 @@ */ /* note: we use upper 8 bits of flags for driver-internal flags: */ -#define OMAP_BO_DMA 0x01000000 /* actually is physically contiguous */ -#define OMAP_BO_EXT_SYNC 0x02000000 /* externally allocated sync object */ -#define OMAP_BO_EXT_MEM 0x04000000 /* externally allocated memory */ +#define OMAP_BO_MEM_DMA_API 0x01000000 /* memory allocated with the dma_alloc_* API */ +#define OMAP_BO_MEM_SHMEM 0x02000000 /* memory allocated through shmem backing */ +#define OMAP_BO_MEM_DMABUF 0x08000000 /* memory imported from a dmabuf */ struct omap_gem_object { struct drm_gem_object base; @@ -49,17 +49,25 @@ struct omap_gem_object { uint32_t roll; /** - * If buffer is allocated physically contiguous, the OMAP_BO_DMA flag - * is set and the paddr is valid. Also if the buffer is remapped in - * TILER and paddr_cnt > 0, then paddr is valid. But if you are using - * the physical address and OMAP_BO_DMA is not set, then you should - * be going thru omap_gem_{get,put}_paddr() to ensure the mapping is - * not removed from under your feet. + * paddr contains the buffer DMA address. It is valid for * - * Note that OMAP_BO_SCANOUT is a hint from userspace that DMA capable - * buffer is requested, but doesn't mean that it is. Use the - * OMAP_BO_DMA flag to determine if the buffer has a DMA capable - * physical address. + * - buffers allocated through the DMA mapping API (with the + * OMAP_BO_MEM_DMA_API flag set) + * + * - buffers imported from dmabuf (with the OMAP_BO_MEM_DMABUF flag set) + * if they are physically contiguous (when sgt->orig_nents == 1) + * + * - buffers mapped through the TILER when paddr_cnt is not zero, in + * which case the DMA address points to the TILER aperture + * + * Physically contiguous buffers have their DMA address equal to the + * physical address as we don't remap those buffers through the TILER. + * + * Buffers mapped to the TILER have their DMA address pointing to the + * TILER aperture. As TILER mappings are refcounted (through paddr_cnt) + * the DMA address must be accessed through omap_get_get_paddr() to + * ensure that the mapping won't disappear unexpectedly. References must + * be released with omap_gem_put_paddr(). */ dma_addr_t paddr; @@ -69,6 +77,12 @@ struct omap_gem_object { uint32_t paddr_cnt; /** + * If the buffer has been imported from a dmabuf the OMAP_DB_DMABUF flag + * is set and the sgt field is valid. + */ + struct sg_table *sgt; + + /** * tiler block used when buffer is remapped in DMM/TILER. */ struct tiler_block *block; @@ -91,17 +105,7 @@ struct omap_gem_object { * sync-object allocated on demand (if needed) * * Per-buffer sync-object for tracking pending and completed hw/dma - * read and write operations. The layout in memory is dictated by - * the SGX firmware, which uses this information to stall the command - * stream if a surface is not ready yet. - * - * Note that when buffer is used by SGX, the sync-object needs to be - * allocated from a special heap of sync-objects. This way many sync - * objects can be packed in a page, and not waste GPU virtual address - * space. Because of this we have to have a omap_gem_set_sync_object() - * API to allow replacement of the syncobj after it has (potentially) - * already been allocated. A bit ugly but I haven't thought of a - * better alternative. + * read and write operations. */ struct { uint32_t write_pending; @@ -166,16 +170,15 @@ static uint64_t mmap_offset(struct drm_gem_object *obj) return drm_vma_node_offset_addr(&obj->vma_node); } -/* GEM objects can either be allocated from contiguous memory (in which - * case obj->filp==NULL), or w/ shmem backing (obj->filp!=NULL). But non - * contiguous buffers can be remapped in TILER/DMM if they need to be - * contiguous... but we don't do this all the time to reduce pressure - * on TILER/DMM space when we know at allocation time that the buffer - * will need to be scanned out. - */ -static inline bool is_shmem(struct drm_gem_object *obj) +static bool is_contiguous(struct omap_gem_object *omap_obj) { - return obj->filp != NULL; + if (omap_obj->flags & OMAP_BO_MEM_DMA_API) + return true; + + if ((omap_obj->flags & OMAP_BO_MEM_DMABUF) && omap_obj->sgt->nents == 1) + return true; + + return false; } /* ----------------------------------------------------------------------------- @@ -264,6 +267,19 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj) for (i = 0; i < npages; i++) { addrs[i] = dma_map_page(dev->dev, pages[i], 0, PAGE_SIZE, DMA_BIDIRECTIONAL); + + if (dma_mapping_error(dev->dev, addrs[i])) { + dev_warn(dev->dev, + "%s: failed to map page\n", __func__); + + for (i = i - 1; i >= 0; --i) { + dma_unmap_page(dev->dev, addrs[i], + PAGE_SIZE, DMA_BIDIRECTIONAL); + } + + ret = -ENOMEM; + goto free_addrs; + } } } else { addrs = kzalloc(npages * sizeof(*addrs), GFP_KERNEL); @@ -278,6 +294,8 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj) return 0; +free_addrs: + kfree(addrs); free_pages: drm_gem_put_pages(obj, pages, true, false); @@ -292,7 +310,7 @@ static int get_pages(struct drm_gem_object *obj, struct page ***pages) struct omap_gem_object *omap_obj = to_omap_bo(obj); int ret = 0; - if (is_shmem(obj) && !omap_obj->pages) { + if ((omap_obj->flags & OMAP_BO_MEM_SHMEM) && !omap_obj->pages) { ret = omap_gem_attach_pages(obj); if (ret) { dev_err(obj->dev->dev, "could not attach pages\n"); @@ -396,7 +414,7 @@ static int fault_1d(struct drm_gem_object *obj, omap_gem_cpu_sync(obj, pgoff); pfn = page_to_pfn(omap_obj->pages[pgoff]); } else { - BUG_ON(!(omap_obj->flags & OMAP_BO_DMA)); + BUG_ON(!is_contiguous(omap_obj)); pfn = (omap_obj->paddr >> PAGE_SHIFT) + pgoff; } @@ -560,6 +578,11 @@ fail: case 0: case -ERESTARTSYS: case -EINTR: + case -EBUSY: + /* + * EBUSY is ok: this just means that another thread + * already did the job. + */ return VM_FAULT_NOPAGE; case -ENOMEM: return VM_FAULT_OOM; @@ -728,7 +751,8 @@ fail: static inline bool is_cached_coherent(struct drm_gem_object *obj) { struct omap_gem_object *omap_obj = to_omap_bo(obj); - return is_shmem(obj) && + + return (omap_obj->flags & OMAP_BO_MEM_SHMEM) && ((omap_obj->flags & OMAP_BO_CACHE_MASK) == OMAP_BO_CACHED); } @@ -761,9 +785,20 @@ void omap_gem_dma_sync(struct drm_gem_object *obj, for (i = 0; i < npages; i++) { if (!omap_obj->addrs[i]) { - omap_obj->addrs[i] = dma_map_page(dev->dev, pages[i], 0, + dma_addr_t addr; + + addr = dma_map_page(dev->dev, pages[i], 0, PAGE_SIZE, DMA_BIDIRECTIONAL); + + if (dma_mapping_error(dev->dev, addr)) { + dev_warn(dev->dev, + "%s: failed to map page\n", + __func__); + break; + } + dirty = true; + omap_obj->addrs[i] = addr; } } @@ -787,7 +822,7 @@ int omap_gem_get_paddr(struct drm_gem_object *obj, mutex_lock(&obj->dev->struct_mutex); - if (remap && is_shmem(obj) && priv->has_dmm) { + if (!is_contiguous(omap_obj) && remap && priv->has_dmm) { if (omap_obj->paddr_cnt == 0) { struct page **pages; uint32_t npages = obj->size >> PAGE_SHIFT; @@ -834,7 +869,7 @@ int omap_gem_get_paddr(struct drm_gem_object *obj, omap_obj->paddr_cnt++; *paddr = omap_obj->paddr; - } else if (omap_obj->flags & OMAP_BO_DMA) { + } else if (is_contiguous(omap_obj)) { *paddr = omap_obj->paddr; } else { ret = -EINVAL; @@ -1138,20 +1173,6 @@ unlock: return ret; } -/* it is a bit lame to handle updates in this sort of polling way, but - * in case of PVR, the GPU can directly update read/write complete - * values, and not really tell us which ones it updated.. this also - * means that sync_lock is not quite sufficient. So we'll need to - * do something a bit better when it comes time to add support for - * separate 2d hw.. - */ -void omap_gem_op_update(void) -{ - spin_lock(&sync_lock); - sync_op_update(); - spin_unlock(&sync_lock); -} - /* mark the start of read and/or write operation */ int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op) { @@ -1219,7 +1240,7 @@ int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op) * is currently blocked.. fxn() can be called from any context * * (TODO for now fxn is called back from whichever context calls - * omap_gem_op_update().. but this could be better defined later + * omap_gem_op_finish().. but this could be better defined later * if needed) * * TODO more code in common w/ _sync().. @@ -1261,50 +1282,10 @@ int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op, return 0; } -/* special API so PVR can update the buffer to use a sync-object allocated - * from it's sync-obj heap. Only used for a newly allocated (from PVR's - * perspective) sync-object, so we overwrite the new syncobj w/ values - * from the already allocated syncobj (if there is one) - */ -int omap_gem_set_sync_object(struct drm_gem_object *obj, void *syncobj) -{ - struct omap_gem_object *omap_obj = to_omap_bo(obj); - int ret = 0; - - spin_lock(&sync_lock); - - if ((omap_obj->flags & OMAP_BO_EXT_SYNC) && !syncobj) { - /* clearing a previously set syncobj */ - syncobj = kmemdup(omap_obj->sync, sizeof(*omap_obj->sync), - GFP_ATOMIC); - if (!syncobj) { - ret = -ENOMEM; - goto unlock; - } - omap_obj->flags &= ~OMAP_BO_EXT_SYNC; - omap_obj->sync = syncobj; - } else if (syncobj && !(omap_obj->flags & OMAP_BO_EXT_SYNC)) { - /* replacing an existing syncobj */ - if (omap_obj->sync) { - memcpy(syncobj, omap_obj->sync, sizeof(*omap_obj->sync)); - kfree(omap_obj->sync); - } - omap_obj->flags |= OMAP_BO_EXT_SYNC; - omap_obj->sync = syncobj; - } - -unlock: - spin_unlock(&sync_lock); - return ret; -} - /* ----------------------------------------------------------------------------- * Constructor & Destructor */ -/* don't call directly.. called from GEM core when it is time to actually - * free the object.. - */ void omap_gem_free_object(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; @@ -1324,22 +1305,23 @@ void omap_gem_free_object(struct drm_gem_object *obj) */ WARN_ON(omap_obj->paddr_cnt > 0); - /* don't free externally allocated backing memory */ - if (!(omap_obj->flags & OMAP_BO_EXT_MEM)) { - if (omap_obj->pages) + if (omap_obj->pages) { + if (omap_obj->flags & OMAP_BO_MEM_DMABUF) + kfree(omap_obj->pages); + else omap_gem_detach_pages(obj); + } - if (!is_shmem(obj)) { - dma_free_writecombine(dev->dev, obj->size, - omap_obj->vaddr, omap_obj->paddr); - } else if (omap_obj->vaddr) { - vunmap(omap_obj->vaddr); - } + if (omap_obj->flags & OMAP_BO_MEM_DMA_API) { + dma_free_writecombine(dev->dev, obj->size, + omap_obj->vaddr, omap_obj->paddr); + } else if (omap_obj->vaddr) { + vunmap(omap_obj->vaddr); + } else if (obj->import_attach) { + drm_prime_gem_destroy(obj, omap_obj->sgt); } - /* don't free externally allocated syncobj */ - if (!(omap_obj->flags & OMAP_BO_EXT_SYNC)) - kfree(omap_obj->sync); + kfree(omap_obj->sync); drm_gem_object_release(obj); @@ -1357,84 +1339,160 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, size_t size; int ret; + /* Validate the flags and compute the memory and cache flags. */ if (flags & OMAP_BO_TILED) { if (!priv->usergart) { dev_err(dev->dev, "Tiled buffers require DMM\n"); return NULL; } - /* tiled buffers are always shmem paged backed.. when they are - * scanned out, they are remapped into DMM/TILER + /* + * Tiled buffers are always shmem paged backed. When they are + * scanned out, they are remapped into DMM/TILER. */ flags &= ~OMAP_BO_SCANOUT; + flags |= OMAP_BO_MEM_SHMEM; - /* currently don't allow cached buffers.. there is some caching - * stuff that needs to be handled better + /* + * Currently don't allow cached buffers. There is some caching + * stuff that needs to be handled better. */ flags &= ~(OMAP_BO_CACHED|OMAP_BO_WC|OMAP_BO_UNCACHED); flags |= tiler_get_cpu_cache_flags(); - - /* align dimensions to slot boundaries... */ - tiler_align(gem2fmt(flags), - &gsize.tiled.width, &gsize.tiled.height); - - /* ...and calculate size based on aligned dimensions */ - size = tiler_size(gem2fmt(flags), - gsize.tiled.width, gsize.tiled.height); - } else { - size = PAGE_ALIGN(gsize.bytes); + } else if ((flags & OMAP_BO_SCANOUT) && !priv->has_dmm) { + /* + * OMAP_BO_SCANOUT hints that the buffer doesn't need to be + * tiled. However, to lower the pressure on memory allocation, + * use contiguous memory only if no TILER is available. + */ + flags |= OMAP_BO_MEM_DMA_API; + } else if (!(flags & OMAP_BO_MEM_DMABUF)) { + /* + * All other buffers not backed by dma_buf are shmem-backed. + */ + flags |= OMAP_BO_MEM_SHMEM; } + /* Allocate the initialize the OMAP GEM object. */ omap_obj = kzalloc(sizeof(*omap_obj), GFP_KERNEL); if (!omap_obj) return NULL; obj = &omap_obj->base; + omap_obj->flags = flags; - if ((flags & OMAP_BO_SCANOUT) && !priv->has_dmm) { - /* attempt to allocate contiguous memory if we don't - * have DMM for remappign discontiguous buffers + if (flags & OMAP_BO_TILED) { + /* + * For tiled buffers align dimensions to slot boundaries and + * calculate size based on aligned dimensions. */ - omap_obj->vaddr = dma_alloc_writecombine(dev->dev, size, - &omap_obj->paddr, GFP_KERNEL); - if (!omap_obj->vaddr) { - kfree(omap_obj); + tiler_align(gem2fmt(flags), &gsize.tiled.width, + &gsize.tiled.height); - return NULL; - } - - flags |= OMAP_BO_DMA; - } + size = tiler_size(gem2fmt(flags), gsize.tiled.width, + gsize.tiled.height); - spin_lock(&priv->list_lock); - list_add(&omap_obj->mm_list, &priv->obj_list); - spin_unlock(&priv->list_lock); - - omap_obj->flags = flags; - - if (flags & OMAP_BO_TILED) { omap_obj->width = gsize.tiled.width; omap_obj->height = gsize.tiled.height; + } else { + size = PAGE_ALIGN(gsize.bytes); } - if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM)) { + /* Initialize the GEM object. */ + if (!(flags & OMAP_BO_MEM_SHMEM)) { drm_gem_private_object_init(dev, obj, size); } else { ret = drm_gem_object_init(dev, obj, size); if (ret) - goto fail; + goto err_free; mapping = file_inode(obj->filp)->i_mapping; mapping_set_gfp_mask(mapping, GFP_USER | __GFP_DMA32); } + /* Allocate memory if needed. */ + if (flags & OMAP_BO_MEM_DMA_API) { + omap_obj->vaddr = dma_alloc_writecombine(dev->dev, size, + &omap_obj->paddr, + GFP_KERNEL); + if (!omap_obj->vaddr) + goto err_release; + } + + spin_lock(&priv->list_lock); + list_add(&omap_obj->mm_list, &priv->obj_list); + spin_unlock(&priv->list_lock); + return obj; -fail: - omap_gem_free_object(obj); +err_release: + drm_gem_object_release(obj); +err_free: + kfree(omap_obj); return NULL; } +struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size, + struct sg_table *sgt) +{ + struct omap_drm_private *priv = dev->dev_private; + struct omap_gem_object *omap_obj; + struct drm_gem_object *obj; + union omap_gem_size gsize; + + /* Without a DMM only physically contiguous buffers can be supported. */ + if (sgt->orig_nents != 1 && !priv->has_dmm) + return ERR_PTR(-EINVAL); + + mutex_lock(&dev->struct_mutex); + + gsize.bytes = PAGE_ALIGN(size); + obj = omap_gem_new(dev, gsize, OMAP_BO_MEM_DMABUF | OMAP_BO_WC); + if (!obj) { + obj = ERR_PTR(-ENOMEM); + goto done; + } + + omap_obj = to_omap_bo(obj); + omap_obj->sgt = sgt; + + if (sgt->orig_nents == 1) { + omap_obj->paddr = sg_dma_address(sgt->sgl); + } else { + /* Create pages list from sgt */ + struct sg_page_iter iter; + struct page **pages; + unsigned int npages; + unsigned int i = 0; + + npages = DIV_ROUND_UP(size, PAGE_SIZE); + pages = kcalloc(npages, sizeof(*pages), GFP_KERNEL); + if (!pages) { + omap_gem_free_object(obj); + obj = ERR_PTR(-ENOMEM); + goto done; + } + + omap_obj->pages = pages; + + for_each_sg_page(sgt->sgl, &iter, sgt->orig_nents, 0) { + pages[i++] = sg_page_iter_page(&iter); + if (i > npages) + break; + } + + if (WARN_ON(i != npages)) { + omap_gem_free_object(obj); + obj = ERR_PTR(-ENOMEM); + goto done; + } + } + +done: + mutex_unlock(&dev->struct_mutex); + return obj; +} + /* convenience method to construct a GEM buffer object, and userspace handle */ int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file, union omap_gem_size gsize, uint32_t flags, uint32_t *handle) diff --git a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c index aebae1c2dab2..3cf8aab23a39 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c @@ -21,6 +21,10 @@ #include "omap_drv.h" +/* ----------------------------------------------------------------------------- + * DMABUF Export + */ + static struct sg_table *omap_gem_map_dma_buf( struct dma_buf_attachment *attachment, enum dma_data_direction dir) @@ -178,15 +182,20 @@ struct dma_buf *omap_gem_prime_export(struct drm_device *dev, return dma_buf_export(&exp_info); } +/* ----------------------------------------------------------------------------- + * DMABUF Import + */ + struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, - struct dma_buf *buffer) + struct dma_buf *dma_buf) { + struct dma_buf_attachment *attach; struct drm_gem_object *obj; + struct sg_table *sgt; + int ret; - /* is this one of own objects? */ - if (buffer->ops == &omap_dmabuf_ops) { - obj = buffer->priv; - /* is it from our device? */ + if (dma_buf->ops == &omap_dmabuf_ops) { + obj = dma_buf->priv; if (obj->dev == dev) { /* * Importing dmabuf exported from out own gem increases @@ -197,9 +206,33 @@ struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, } } - /* - * TODO add support for importing buffers from other devices.. - * for now we don't need this but would be nice to add eventually - */ - return ERR_PTR(-EINVAL); + attach = dma_buf_attach(dma_buf, dev->dev); + if (IS_ERR(attach)) + return ERR_CAST(attach); + + get_dma_buf(dma_buf); + + sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + goto fail_detach; + } + + obj = omap_gem_new_dmabuf(dev, dma_buf->size, sgt); + if (IS_ERR(obj)) { + ret = PTR_ERR(obj); + goto fail_unmap; + } + + obj->import_attach = attach; + + return obj; + +fail_unmap: + dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); +fail_detach: + dma_buf_detach(dma_buf, attach); + dma_buf_put(dma_buf); + + return ERR_PTR(ret); } diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index d75b197eff46..93ee538a99f5 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -177,6 +177,12 @@ static int omap_plane_atomic_check(struct drm_plane *plane, if (state->crtc_y + state->crtc_h > crtc_state->adjusted_mode.vdisplay) return -EINVAL; + if (state->fb) { + if (state->rotation != BIT(DRM_ROTATE_0) && + !omap_framebuffer_supports_rotation(state->fb)) + return -EINVAL; + } + return 0; } diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index f88a631c43ab..2164c999052c 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -1016,6 +1016,7 @@ static const struct drm_display_mode nec_nl4827hc19_05b_mode = { .vsync_end = 272 + 2 + 4, .vtotal = 272 + 2 + 4 + 2, .vrefresh = 74, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, }; static const struct panel_desc nec_nl4827hc19_05b = { diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index cad25557650f..ccd4ad4ee592 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -322,6 +322,14 @@ static int radeon_pci_probe(struct pci_dev *pdev, int ret; /* + * Initialize amdkfd before starting radeon. If it was not loaded yet, + * defer radeon probing + */ + ret = radeon_kfd_init(); + if (ret == -EPROBE_DEFER) + return ret; + + /* * apple-gmux is needed on dual GPU MacBook Pro * to probe the panel if we're the inactive GPU. */ @@ -581,8 +589,6 @@ static int __init radeon_init(void) return -EINVAL; } - radeon_kfd_init(); - /* let modprobe override vga console setting */ return drm_pci_init(driver, pdriver); } diff --git a/drivers/gpu/drm/radeon/radeon_kfd.c b/drivers/gpu/drm/radeon/radeon_kfd.c index 9a4d69e59401..87a9ebb5f58f 100644 --- a/drivers/gpu/drm/radeon/radeon_kfd.c +++ b/drivers/gpu/drm/radeon/radeon_kfd.c @@ -132,35 +132,34 @@ static const struct kfd2kgd_calls kfd2kgd = { static const struct kgd2kfd_calls *kgd2kfd; -bool radeon_kfd_init(void) +int radeon_kfd_init(void) { + int ret; + #if defined(CONFIG_HSA_AMD_MODULE) - bool (*kgd2kfd_init_p)(unsigned, const struct kgd2kfd_calls**); + int (*kgd2kfd_init_p)(unsigned, const struct kgd2kfd_calls**); kgd2kfd_init_p = symbol_request(kgd2kfd_init); if (kgd2kfd_init_p == NULL) - return false; + return -ENOENT; - if (!kgd2kfd_init_p(KFD_INTERFACE_VERSION, &kgd2kfd)) { + ret = kgd2kfd_init_p(KFD_INTERFACE_VERSION, &kgd2kfd); + if (ret) { symbol_put(kgd2kfd_init); kgd2kfd = NULL; - - return false; } - return true; #elif defined(CONFIG_HSA_AMD) - if (!kgd2kfd_init(KFD_INTERFACE_VERSION, &kgd2kfd)) { + ret = kgd2kfd_init(KFD_INTERFACE_VERSION, &kgd2kfd); + if (ret) kgd2kfd = NULL; - return false; - } - - return true; #else - return false; + ret = -ENOENT; #endif + + return ret; } void radeon_kfd_fini(void) diff --git a/drivers/gpu/drm/radeon/radeon_kfd.h b/drivers/gpu/drm/radeon/radeon_kfd.h index 1103f9082f6b..9df1fea8e971 100644 --- a/drivers/gpu/drm/radeon/radeon_kfd.h +++ b/drivers/gpu/drm/radeon/radeon_kfd.h @@ -33,7 +33,7 @@ struct radeon_device; -bool radeon_kfd_init(void); +int radeon_kfd_init(void); void radeon_kfd_fini(void); void radeon_kfd_suspend(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig index 96dcd4a78951..1f10fa0928b4 100644 --- a/drivers/gpu/drm/rcar-du/Kconfig +++ b/drivers/gpu/drm/rcar-du/Kconfig @@ -1,6 +1,7 @@ config DRM_RCAR_DU tristate "DRM Support for R-Car Display Unit" - depends on DRM && ARM && OF + depends on DRM && OF + depends on ARM || ARM64 depends on ARCH_SHMOBILE || COMPILE_TEST select DRM_KMS_HELPER select DRM_KMS_CMA_HELPER @@ -14,14 +15,18 @@ config DRM_RCAR_DU config DRM_RCAR_HDMI bool "R-Car DU HDMI Encoder Support" depends on DRM_RCAR_DU - depends on OF help Enable support for external HDMI encoders. config DRM_RCAR_LVDS bool "R-Car DU LVDS Encoder Support" depends on DRM_RCAR_DU - depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST help - Enable support for the R-Car Display Unit embedded LVDS encoders - (currently only on R8A7790 and R8A7791). + Enable support for the R-Car Display Unit embedded LVDS encoders. + +config DRM_RCAR_VSP + bool "R-Car DU VSP Compositor Support" + depends on DRM_RCAR_DU + depends on VIDEO_RENESAS_VSP1 + help + Enable support to expose the R-Car VSP Compositor as KMS planes. diff --git a/drivers/gpu/drm/rcar-du/Makefile b/drivers/gpu/drm/rcar-du/Makefile index 05de1c4097af..827711e28226 100644 --- a/drivers/gpu/drm/rcar-du/Makefile +++ b/drivers/gpu/drm/rcar-du/Makefile @@ -11,4 +11,6 @@ rcar-du-drm-$(CONFIG_DRM_RCAR_HDMI) += rcar_du_hdmicon.o \ rcar_du_hdmienc.o rcar-du-drm-$(CONFIG_DRM_RCAR_LVDS) += rcar_du_lvdsenc.o +rcar-du-drm-$(CONFIG_DRM_RCAR_VSP) += rcar_du_vsp.o + obj-$(CONFIG_DRM_RCAR_DU) += rcar-du-drm.o diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 4ec80ae1fa99..51e9e8ce551a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -1,7 +1,7 @@ /* * rcar_du_crtc.c -- R-Car Display Unit CRTCs * - * Copyright (C) 2013-2014 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -28,6 +28,7 @@ #include "rcar_du_kms.h" #include "rcar_du_plane.h" #include "rcar_du_regs.h" +#include "rcar_du_vsp.h" static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg) { @@ -150,7 +151,7 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) /* Signal polarities */ value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL) | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL) - | DSMR_DIPM_DE | DSMR_CSPM; + | DSMR_DIPM_DISP | DSMR_CSPM; rcar_du_crtc_write(rcrtc, DSMR, value); /* Display timings */ @@ -207,6 +208,7 @@ plane_format(struct rcar_du_plane *plane) static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc) { struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES]; + struct rcar_du_device *rcdu = rcrtc->group->dev; unsigned int num_planes = 0; unsigned int dptsr_planes; unsigned int hwplanes = 0; @@ -250,6 +252,17 @@ static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc) } } + /* If VSP+DU integration is enabled the plane assignment is fixed. */ + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) { + if (rcdu->info->gen < 3) { + dspr = (rcrtc->index % 2) + 1; + hwplanes = 1 << (rcrtc->index % 2); + } else { + dspr = (rcrtc->index % 2) ? 3 : 1; + hwplanes = 1 << ((rcrtc->index % 2) ? 2 : 0); + } + } + /* Update the planes to display timing and dot clock generator * associations. * @@ -272,6 +285,10 @@ static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc) rcar_du_group_restart(rcrtc->group); } + /* Restart the group if plane sources have changed. */ + if (rcrtc->group->need_restart) + rcar_du_group_restart(rcrtc->group); + mutex_unlock(&rcrtc->group->lock); rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, @@ -365,6 +382,10 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) rcar_du_group_start_stop(rcrtc->group, true); + /* Enable the VSP compositor. */ + if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) + rcar_du_vsp_enable(rcrtc); + /* Turn vertical blanking interrupt reporting back on. */ drm_crtc_vblank_on(crtc); @@ -398,6 +419,10 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) rcar_du_crtc_wait_page_flip(rcrtc); drm_crtc_vblank_off(crtc); + /* Disable the VSP compositor. */ + if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) + rcar_du_vsp_disable(rcrtc); + /* Select switch sync mode. This stops display operation and configures * the HSYNC and VSYNC signals as inputs. */ @@ -410,6 +435,9 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc) { + if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) + rcar_du_vsp_disable(rcrtc); + rcar_du_crtc_stop(rcrtc); rcar_du_crtc_put(rcrtc); } @@ -418,20 +446,24 @@ void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc) { unsigned int i; - if (!rcrtc->enabled) + if (!rcrtc->crtc.state->active) return; rcar_du_crtc_get(rcrtc); rcar_du_crtc_start(rcrtc); /* Commit the planes state. */ - for (i = 0; i < rcrtc->group->num_planes; ++i) { - struct rcar_du_plane *plane = &rcrtc->group->planes[i]; + if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) { + rcar_du_vsp_enable(rcrtc); + } else { + for (i = 0; i < rcrtc->group->num_planes; ++i) { + struct rcar_du_plane *plane = &rcrtc->group->planes[i]; - if (plane->plane.state->crtc != &rcrtc->crtc) - continue; + if (plane->plane.state->crtc != &rcrtc->crtc) + continue; - rcar_du_plane_setup(plane); + rcar_du_plane_setup(plane); + } } rcar_du_crtc_update_planes(rcrtc); @@ -445,26 +477,17 @@ static void rcar_du_crtc_enable(struct drm_crtc *crtc) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - if (rcrtc->enabled) - return; - rcar_du_crtc_get(rcrtc); rcar_du_crtc_start(rcrtc); - - rcrtc->enabled = true; } static void rcar_du_crtc_disable(struct drm_crtc *crtc) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); - if (!rcrtc->enabled) - return; - rcar_du_crtc_stop(rcrtc); rcar_du_crtc_put(rcrtc); - rcrtc->enabled = false; rcrtc->outputs = 0; } @@ -491,6 +514,9 @@ static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc, rcrtc->event = event; spin_unlock_irqrestore(&dev->event_lock, flags); } + + if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) + rcar_du_vsp_atomic_begin(rcrtc); } static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc, @@ -499,6 +525,9 @@ static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc, struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); rcar_du_crtc_update_planes(rcrtc); + + if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) + rcar_du_vsp_atomic_flush(rcrtc); } static const struct drm_crtc_helper_funcs crtc_helper_funcs = { @@ -547,13 +576,14 @@ static irqreturn_t rcar_du_crtc_irq(int irq, void *arg) int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) { static const unsigned int mmio_offsets[] = { - DU0_REG_OFFSET, DU1_REG_OFFSET, DU2_REG_OFFSET + DU0_REG_OFFSET, DU1_REG_OFFSET, DU2_REG_OFFSET, DU3_REG_OFFSET }; struct rcar_du_device *rcdu = rgrp->dev; struct platform_device *pdev = to_platform_device(rcdu->dev); struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index]; struct drm_crtc *crtc = &rcrtc->crtc; + struct drm_plane *primary; unsigned int irqflags; struct clk *clk; char clk_name[9]; @@ -589,10 +619,13 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index) rcrtc->group = rgrp; rcrtc->mmio_offset = mmio_offsets[index]; rcrtc->index = index; - rcrtc->enabled = false; - ret = drm_crtc_init_with_planes(rcdu->ddev, crtc, - &rgrp->planes[index % 2].plane, + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) + primary = &rcrtc->vsp->planes[0].plane; + else + primary = &rgrp->planes[index % 2].plane; + + ret = drm_crtc_init_with_planes(rcdu->ddev, crtc, primary, NULL, &crtc_funcs, NULL); if (ret < 0) return ret; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 2bbe3f5aab65..6f08b7e7db06 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h @@ -21,6 +21,7 @@ #include <drm/drm_crtc.h> struct rcar_du_group; +struct rcar_du_vsp; /** * struct rcar_du_crtc - the CRTC, representing a DU superposition processor @@ -33,7 +34,6 @@ struct rcar_du_group; * @event: event to post when the pending page flip completes * @flip_wait: wait queue used to signal page flip completion * @outputs: bitmask of the outputs (enum rcar_du_output) driven by this CRTC - * @enabled: whether the CRTC is enabled, used to control system resume * @group: CRTC group this CRTC belongs to */ struct rcar_du_crtc { @@ -49,9 +49,9 @@ struct rcar_du_crtc { wait_queue_head_t flip_wait; unsigned int outputs; - bool enabled; struct rcar_du_group *group; + struct rcar_du_vsp *vsp; }; #define to_rcar_crtc(c) container_of(c, struct rcar_du_crtc, crtc) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 0bb2b31555bf..ed6006bf6bd8 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -1,7 +1,7 @@ /* * rcar_du_drv.c -- R-Car Display Unit DRM driver * - * Copyright (C) 2013-2014 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -36,6 +36,7 @@ */ static const struct rcar_du_device_info rcar_du_r8a7779_info = { + .gen = 2, .features = 0, .num_crtcs = 2, .routes = { @@ -57,6 +58,7 @@ static const struct rcar_du_device_info rcar_du_r8a7779_info = { }; static const struct rcar_du_device_info rcar_du_r8a7790_info = { + .gen = 2, .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_EXT_CTRL_REGS, .quirks = RCAR_DU_QUIRK_ALIGN_128B | RCAR_DU_QUIRK_LVDS_LANES, @@ -86,6 +88,7 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = { /* M2-W (r8a7791) and M2-N (r8a7793) are identical */ static const struct rcar_du_device_info rcar_du_r8a7791_info = { + .gen = 2, .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_EXT_CTRL_REGS, .num_crtcs = 2, @@ -108,6 +111,7 @@ static const struct rcar_du_device_info rcar_du_r8a7791_info = { }; static const struct rcar_du_device_info rcar_du_r8a7794_info = { + .gen = 2, .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | RCAR_DU_FEATURE_EXT_CTRL_REGS, .num_crtcs = 2, @@ -129,12 +133,37 @@ static const struct rcar_du_device_info rcar_du_r8a7794_info = { .num_lvds = 0, }; +static const struct rcar_du_device_info rcar_du_r8a7795_info = { + .gen = 3, + .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK + | RCAR_DU_FEATURE_EXT_CTRL_REGS + | RCAR_DU_FEATURE_VSP1_SOURCE, + .num_crtcs = 4, + .routes = { + /* R8A7795 has one RGB output, one LVDS output and two + * (currently unsupported) HDMI outputs. + */ + [RCAR_DU_OUTPUT_DPAD0] = { + .possible_crtcs = BIT(3), + .encoder_type = DRM_MODE_ENCODER_NONE, + .port = 0, + }, + [RCAR_DU_OUTPUT_LVDS0] = { + .possible_crtcs = BIT(0), + .encoder_type = DRM_MODE_ENCODER_LVDS, + .port = 3, + }, + }, + .num_lvds = 1, +}; + static const struct of_device_id rcar_du_of_table[] = { { .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 }, { .compatible = "renesas,du-r8a7793", .data = &rcar_du_r8a7791_info }, { .compatible = "renesas,du-r8a7794", .data = &rcar_du_r8a7794_info }, + { .compatible = "renesas,du-r8a7795", .data = &rcar_du_r8a7795_info }, { } }; @@ -144,82 +173,6 @@ MODULE_DEVICE_TABLE(of, rcar_du_of_table); * DRM operations */ -static int rcar_du_unload(struct drm_device *dev) -{ - struct rcar_du_device *rcdu = dev->dev_private; - - if (rcdu->fbdev) - drm_fbdev_cma_fini(rcdu->fbdev); - - drm_kms_helper_poll_fini(dev); - drm_mode_config_cleanup(dev); - drm_vblank_cleanup(dev); - - dev->irq_enabled = 0; - dev->dev_private = NULL; - - return 0; -} - -static int rcar_du_load(struct drm_device *dev, unsigned long flags) -{ - struct platform_device *pdev = dev->platformdev; - struct device_node *np = pdev->dev.of_node; - struct rcar_du_device *rcdu; - struct resource *mem; - int ret; - - if (np == NULL) { - dev_err(dev->dev, "no platform data\n"); - return -ENODEV; - } - - rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL); - if (rcdu == NULL) { - dev_err(dev->dev, "failed to allocate private data\n"); - return -ENOMEM; - } - - init_waitqueue_head(&rcdu->commit.wait); - - rcdu->dev = &pdev->dev; - rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data; - rcdu->ddev = dev; - dev->dev_private = rcdu; - - /* I/O resources */ - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem); - if (IS_ERR(rcdu->mmio)) - return PTR_ERR(rcdu->mmio); - - /* Initialize vertical blanking interrupts handling. Start with vblank - * disabled for all CRTCs. - */ - ret = drm_vblank_init(dev, (1 << rcdu->info->num_crtcs) - 1); - if (ret < 0) { - dev_err(&pdev->dev, "failed to initialize vblank\n"); - goto done; - } - - /* DRM/KMS objects */ - ret = rcar_du_modeset_init(rcdu); - if (ret < 0) { - dev_err(&pdev->dev, "failed to initialize DRM/KMS (%d)\n", ret); - goto done; - } - - dev->irq_enabled = 1; - - platform_set_drvdata(pdev, rcdu); - -done: - if (ret) - rcar_du_unload(dev); - - return ret; -} - static void rcar_du_lastclose(struct drm_device *dev) { struct rcar_du_device *rcdu = dev->dev_private; @@ -260,10 +213,7 @@ static const struct file_operations rcar_du_fops = { static struct drm_driver rcar_du_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, - .load = rcar_du_load, - .unload = rcar_du_unload, .lastclose = rcar_du_lastclose, - .set_busid = drm_platform_set_busid, .get_vblank_counter = drm_vblank_no_hw_counter, .enable_vblank = rcar_du_enable_vblank, .disable_vblank = rcar_du_disable_vblank, @@ -323,18 +273,116 @@ static const struct dev_pm_ops rcar_du_pm_ops = { * Platform driver */ -static int rcar_du_probe(struct platform_device *pdev) +static int rcar_du_remove(struct platform_device *pdev) { - return drm_platform_init(&rcar_du_driver, pdev); + struct rcar_du_device *rcdu = platform_get_drvdata(pdev); + struct drm_device *ddev = rcdu->ddev; + + mutex_lock(&ddev->mode_config.mutex); + drm_connector_unplug_all(ddev); + mutex_unlock(&ddev->mode_config.mutex); + + 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_vblank_cleanup(ddev); + + drm_dev_unref(ddev); + + return 0; } -static int rcar_du_remove(struct platform_device *pdev) +static int rcar_du_probe(struct platform_device *pdev) { - struct rcar_du_device *rcdu = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node; + struct rcar_du_device *rcdu; + struct drm_connector *connector; + struct drm_device *ddev; + struct resource *mem; + int ret; - drm_put_dev(rcdu->ddev); + if (np == NULL) { + dev_err(&pdev->dev, "no device tree node\n"); + return -ENODEV; + } + + /* Allocate and initialize the DRM and R-Car device structures. */ + rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL); + if (rcdu == NULL) + return -ENOMEM; + + init_waitqueue_head(&rcdu->commit.wait); + + rcdu->dev = &pdev->dev; + rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data; + + ddev = drm_dev_alloc(&rcar_du_driver, &pdev->dev); + if (!ddev) + return -ENOMEM; + + drm_dev_set_unique(ddev, dev_name(&pdev->dev)); + + rcdu->ddev = ddev; + ddev->dev_private = rcdu; + + platform_set_drvdata(pdev, rcdu); + + /* I/O resources */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(rcdu->mmio)) { + ret = PTR_ERR(rcdu->mmio); + goto error; + } + + /* Initialize vertical blanking interrupts handling. Start with vblank + * disabled for all CRTCs. + */ + ret = drm_vblank_init(ddev, (1 << rcdu->info->num_crtcs) - 1); + if (ret < 0) { + dev_err(&pdev->dev, "failed to initialize vblank\n"); + goto error; + } + + /* DRM/KMS objects */ + ret = rcar_du_modeset_init(rcdu); + if (ret < 0) { + dev_err(&pdev->dev, "failed to initialize DRM/KMS (%d)\n", ret); + goto error; + } + + ddev->irq_enabled = 1; + + /* Register the DRM device with the core and the connectors with + * sysfs. + */ + ret = drm_dev_register(ddev, 0); + if (ret) + goto error; + + mutex_lock(&ddev->mode_config.mutex); + drm_for_each_connector(connector, ddev) { + ret = drm_connector_register(connector); + if (ret < 0) + break; + } + mutex_unlock(&ddev->mode_config.mutex); + + if (ret < 0) + goto error; + + DRM_INFO("Device %s probed\n", dev_name(&pdev->dev)); return 0; + +error: + rcar_du_remove(pdev); + + return ret; } static struct platform_driver rcar_du_platform_driver = { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 9f34fc86436a..ed35467d96cf 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -1,7 +1,7 @@ /* * rcar_du_drv.h -- R-Car Display Unit DRM driver * - * Copyright (C) 2013-2014 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -19,6 +19,7 @@ #include "rcar_du_crtc.h" #include "rcar_du_group.h" +#include "rcar_du_vsp.h" struct clk; struct device; @@ -29,6 +30,7 @@ struct rcar_du_lvdsenc; #define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK (1 << 0) /* Per-CRTC IRQ and clock */ #define RCAR_DU_FEATURE_EXT_CTRL_REGS (1 << 1) /* Has extended control registers */ +#define RCAR_DU_FEATURE_VSP1_SOURCE (1 << 2) /* Has inputs from VSP1 */ #define RCAR_DU_QUIRK_ALIGN_128B (1 << 0) /* Align pitches to 128 bytes */ #define RCAR_DU_QUIRK_LVDS_LANES (1 << 1) /* LVDS lanes 1 and 3 inverted */ @@ -51,6 +53,7 @@ struct rcar_du_output_routing { /* * struct rcar_du_device_info - DU model-specific information + * @gen: device generation (2 or 3) * @features: device features (RCAR_DU_FEATURE_*) * @quirks: device quirks (RCAR_DU_QUIRK_*) * @num_crtcs: total number of CRTCs @@ -58,6 +61,7 @@ struct rcar_du_output_routing { * @num_lvds: number of internal LVDS encoders */ struct rcar_du_device_info { + unsigned int gen; unsigned int features; unsigned int quirks; unsigned int num_crtcs; @@ -65,9 +69,10 @@ struct rcar_du_device_info { unsigned int num_lvds; }; -#define RCAR_DU_MAX_CRTCS 3 +#define RCAR_DU_MAX_CRTCS 4 #define RCAR_DU_MAX_GROUPS DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2) #define RCAR_DU_MAX_LVDS 2 +#define RCAR_DU_MAX_VSPS 4 struct rcar_du_device { struct device *dev; @@ -82,6 +87,7 @@ struct rcar_du_device { unsigned int num_crtcs; struct rcar_du_group groups[RCAR_DU_MAX_GROUPS]; + struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS]; struct { struct drm_property *alpha; @@ -90,6 +96,8 @@ struct rcar_du_device { } props; unsigned int dpad0_source; + unsigned int vspd1_sink; + struct rcar_du_lvdsenc *lvds[RCAR_DU_MAX_LVDS]; struct { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index c08700757feb..4e939e41f030 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -89,12 +89,8 @@ static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder, /* The flat panel mode is fixed, just copy it to the adjusted mode. */ drm_mode_copy(adjusted_mode, panel_mode); - /* The internal LVDS encoder has a clock frequency operating range of - * 30MHz to 150MHz. Clamp the clock accordingly. - */ if (renc->lvds) - adjusted_mode->clock = clamp(adjusted_mode->clock, - 30000, 150000); + rcar_du_lvdsenc_atomic_check(renc->lvds, adjusted_mode); return 0; } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c index 8e2ffe025153..33b2fc53da3e 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c @@ -1,7 +1,7 @@ /* * rcar_du_group.c -- R-Car Display Unit Channels Pair * - * Copyright (C) 2013-2014 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -44,29 +44,64 @@ void rcar_du_group_write(struct rcar_du_group *rgrp, u32 reg, u32 data) rcar_du_write(rgrp->dev, rgrp->mmio_offset + reg, data); } +static void rcar_du_group_setup_pins(struct rcar_du_group *rgrp) +{ + u32 defr6 = DEFR6_CODE | DEFR6_ODPM12_DISP; + + if (rgrp->num_crtcs > 1) + defr6 |= DEFR6_ODPM22_DISP; + + rcar_du_group_write(rgrp, DEFR6, defr6); +} + static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp) { - u32 defr8 = DEFR8_CODE | DEFR8_DEFE8; + struct rcar_du_device *rcdu = rgrp->dev; + unsigned int possible_crtcs = + rcdu->info->routes[RCAR_DU_OUTPUT_DPAD0].possible_crtcs; + u32 defr8 = DEFR8_CODE; - /* The DEFR8 register for the first group also controls RGB output - * routing to DPAD0 for DU instances that support it. - */ - if (rgrp->dev->info->routes[RCAR_DU_OUTPUT_DPAD0].possible_crtcs > 1 && - rgrp->index == 0) - defr8 |= DEFR8_DRGBS_DU(rgrp->dev->dpad0_source); + if (rcdu->info->gen < 3) { + defr8 |= DEFR8_DEFE8; + + /* On Gen2 the DEFR8 register for the first group also controls + * RGB output routing to DPAD0 and VSPD1 routing to DU0/1/2 for + * DU instances that support it. + */ + if (rgrp->index == 0) { + if (possible_crtcs > 1) + defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source); + if (rgrp->dev->vspd1_sink == 2) + defr8 |= DEFR8_VSCS; + } + } else { + /* On Gen3 VSPD routing can't be configured, but DPAD routing + * needs to be set despite having a single option available. + */ + u32 crtc = ffs(possible_crtcs) - 1; + + if (crtc / 2 == rgrp->index) + defr8 |= DEFR8_DRGBS_DU(crtc); + } rcar_du_group_write(rgrp, DEFR8, defr8); } static void rcar_du_group_setup(struct rcar_du_group *rgrp) { + struct rcar_du_device *rcdu = rgrp->dev; + /* Enable extended features */ rcar_du_group_write(rgrp, DEFR, DEFR_CODE | DEFR_DEFE); - rcar_du_group_write(rgrp, DEFR2, DEFR2_CODE | DEFR2_DEFE2G); - rcar_du_group_write(rgrp, DEFR3, DEFR3_CODE | DEFR3_DEFE3); - rcar_du_group_write(rgrp, DEFR4, DEFR4_CODE); + if (rcdu->info->gen < 3) { + rcar_du_group_write(rgrp, DEFR2, DEFR2_CODE | DEFR2_DEFE2G); + rcar_du_group_write(rgrp, DEFR3, DEFR3_CODE | DEFR3_DEFE3); + rcar_du_group_write(rgrp, DEFR4, DEFR4_CODE); + } rcar_du_group_write(rgrp, DEFR5, DEFR5_CODE | DEFR5_DEFE5); + rcar_du_group_setup_pins(rgrp); + if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_EXT_CTRL_REGS)) { rcar_du_group_setup_defr8(rgrp); @@ -82,6 +117,9 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp) DIDSR_PDCS_CLK(0, 0)); } + if (rcdu->info->gen >= 3) + rcar_du_group_write(rgrp, DEFR10, DEFR10_CODE | DEFR10_DEFE10); + /* Use DS1PR and DS2PR to configure planes priorities and connects the * superposition 0 to DU0 pins. DU1 pins will be configured dynamically. */ @@ -158,21 +196,23 @@ void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start) void rcar_du_group_restart(struct rcar_du_group *rgrp) { + rgrp->need_restart = false; + __rcar_du_group_start_stop(rgrp, false); __rcar_du_group_start_stop(rgrp, true); } -static int rcar_du_set_dpad0_routing(struct rcar_du_device *rcdu) +int rcar_du_set_dpad0_vsp1_routing(struct rcar_du_device *rcdu) { int ret; if (!rcar_du_has(rcdu, RCAR_DU_FEATURE_EXT_CTRL_REGS)) return 0; - /* RGB output routing to DPAD0 is configured in the DEFR8 register of - * the first group. As this function can be called with the DU0 and DU1 - * CRTCs disabled, we need to enable the first group clock before - * accessing the register. + /* RGB output routing to DPAD0 and VSP1D routing to DU0/1/2 are + * configured in the DEFR8 register of the first group. As this function + * can be called with the DU0 and DU1 CRTCs disabled, we need to enable + * the first group clock before accessing the register. */ ret = clk_prepare_enable(rcdu->crtcs[0].clock); if (ret < 0) @@ -203,5 +243,5 @@ int rcar_du_group_set_routing(struct rcar_du_group *rgrp) rcar_du_group_write(rgrp, DORCR, dorcr); - return rcar_du_set_dpad0_routing(rgrp->dev); + return rcar_du_set_dpad0_vsp1_routing(rgrp->dev); } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.h b/drivers/gpu/drm/rcar-du/rcar_du_group.h index d7318e1a6b00..5e3adc6b31b5 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.h @@ -32,6 +32,7 @@ struct rcar_du_device; * @dptsr_planes: bitmask of planes driven by dot-clock and timing generator 1 * @num_planes: number of planes in the group * @planes: planes handled by the group + * @need_restart: the group needs to be restarted due to a configuration change */ struct rcar_du_group { struct rcar_du_device *dev; @@ -47,6 +48,7 @@ struct rcar_du_group { unsigned int num_planes; struct rcar_du_plane planes[RCAR_DU_NUM_KMS_PLANES]; + bool need_restart; }; u32 rcar_du_group_read(struct rcar_du_group *rgrp, u32 reg); @@ -58,4 +60,6 @@ void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start); void rcar_du_group_restart(struct rcar_du_group *rgrp); int rcar_du_group_set_routing(struct rcar_du_group *rgrp); +int rcar_du_set_dpad0_vsp1_routing(struct rcar_du_device *rcdu); + #endif /* __RCAR_DU_GROUP_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c index a37b6e2fe51a..6c927144b5c9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c @@ -55,12 +55,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { .best_encoder = rcar_du_connector_best_encoder, }; -static void rcar_du_hdmi_connector_destroy(struct drm_connector *connector) -{ - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - static enum drm_connector_status rcar_du_hdmi_connector_detect(struct drm_connector *connector, bool force) { @@ -79,7 +73,7 @@ static const struct drm_connector_funcs connector_funcs = { .reset = drm_atomic_helper_connector_reset, .detect = rcar_du_hdmi_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = rcar_du_hdmi_connector_destroy, + .destroy = drm_connector_cleanup, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; @@ -108,9 +102,6 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu, return ret; drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_connector_register(connector); - if (ret < 0) - return ret; connector->dpms = DRM_MODE_DPMS_OFF; drm_object_property_set_value(&connector->base, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c index 2567efcbee36..461662d231e2 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c @@ -71,12 +71,9 @@ static int rcar_du_hdmienc_atomic_check(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; const struct drm_display_mode *mode = &crtc_state->mode; - /* The internal LVDS encoder has a clock frequency operating range of - * 30MHz to 150MHz. Clamp the clock accordingly. - */ if (hdmienc->renc->lvds) - adjusted_mode->clock = clamp(adjusted_mode->clock, - 30000, 150000); + rcar_du_lvdsenc_atomic_check(hdmienc->renc->lvds, + adjusted_mode); if (sfuncs->mode_fixup == NULL) return 0; @@ -134,12 +131,19 @@ int rcar_du_hdmienc_init(struct rcar_du_device *rcdu, /* Locate the slave I2C device and driver. */ i2c_slave = of_find_i2c_device_by_node(np); - if (!i2c_slave || !i2c_get_clientdata(i2c_slave)) + if (!i2c_slave || !i2c_get_clientdata(i2c_slave)) { + dev_dbg(rcdu->dev, + "can't get I2C slave for %s, deferring probe\n", + of_node_full_name(np)); return -EPROBE_DEFER; + } hdmienc->dev = &i2c_slave->dev; if (hdmienc->dev->driver == NULL) { + dev_dbg(rcdu->dev, + "I2C slave %s not probed yet, deferring probe\n", + dev_name(hdmienc->dev)); ret = -EPROBE_DEFER; goto error; } diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 43bce69d8560..24725bf859b4 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -1,7 +1,7 @@ /* * rcar_du_kms.c -- R-Car Display Unit Mode Setting * - * Copyright (C) 2013-2014 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -28,6 +28,7 @@ #include "rcar_du_kms.h" #include "rcar_du_lvdsenc.h" #include "rcar_du_regs.h" +#include "rcar_du_vsp.h" /* ----------------------------------------------------------------------------- * Format helpers @@ -89,13 +90,44 @@ static const struct rcar_du_format_info rcar_du_format_infos[] = { .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC, .edf = PnDDCR4_EDF_NONE, }, { - /* In YUV 4:2:2, only NV16 is supported (NV61 isn't) */ .fourcc = DRM_FORMAT_NV16, .bpp = 16, .planes = 2, .pnmr = PnMR_SPIM_TP_OFF | PnMR_DDDF_YC, .edf = PnDDCR4_EDF_NONE, }, + /* The following formats are not supported on Gen2 and thus have no + * associated .pnmr or .edf settings. + */ + { + .fourcc = DRM_FORMAT_NV61, + .bpp = 16, + .planes = 2, + }, { + .fourcc = DRM_FORMAT_YUV420, + .bpp = 12, + .planes = 3, + }, { + .fourcc = DRM_FORMAT_YVU420, + .bpp = 12, + .planes = 3, + }, { + .fourcc = DRM_FORMAT_YUV422, + .bpp = 16, + .planes = 3, + }, { + .fourcc = DRM_FORMAT_YVU422, + .bpp = 16, + .planes = 3, + }, { + .fourcc = DRM_FORMAT_YUV444, + .bpp = 24, + .planes = 3, + }, { + .fourcc = DRM_FORMAT_YVU444, + .bpp = 24, + .planes = 3, + }, }; const struct rcar_du_format_info *rcar_du_format_info(u32 fourcc) @@ -143,6 +175,7 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, unsigned int max_pitch; unsigned int align; unsigned int bpp; + unsigned int i; format = rcar_du_format_info(mode_cmd->pixel_format); if (format == NULL) { @@ -155,7 +188,7 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, * The pitch and alignment constraints are expressed in pixels on the * hardware side and in bytes in the DRM API. */ - bpp = format->planes == 2 ? 1 : format->bpp / 8; + bpp = format->planes == 1 ? format->bpp / 8 : 1; max_pitch = 4096 * bpp; if (rcar_du_needs(rcdu, RCAR_DU_QUIRK_ALIGN_128B)) @@ -170,8 +203,8 @@ rcar_du_fb_create(struct drm_device *dev, struct drm_file *file_priv, return ERR_PTR(-EINVAL); } - if (format->planes == 2) { - if (mode_cmd->pitches[1] != mode_cmd->pitches[0]) { + for (i = 1; i < format->planes; ++i) { + if (mode_cmd->pitches[i] != mode_cmd->pitches[0]) { dev_dbg(dev->dev, "luma and chroma pitches do not match\n"); return ERR_PTR(-EINVAL); @@ -192,252 +225,20 @@ static void rcar_du_output_poll_changed(struct drm_device *dev) * Atomic Check and Update */ -/* - * Atomic hardware plane allocator - * - * The hardware plane allocator is solely based on the atomic plane states - * without keeping any external state to avoid races between .atomic_check() - * and .atomic_commit(). - * - * The core idea is to avoid using a free planes bitmask that would need to be - * shared between check and commit handlers with a collective knowledge based on - * the allocated hardware plane(s) for each KMS plane. The allocator then loops - * over all plane states to compute the free planes bitmask, allocates hardware - * planes based on that bitmask, and stores the result back in the plane states. - * - * For this to work we need to access the current state of planes not touched by - * the atomic update. To ensure that it won't be modified, we need to lock all - * planes using drm_atomic_get_plane_state(). This effectively serializes atomic - * updates from .atomic_check() up to completion (when swapping the states if - * the check step has succeeded) or rollback (when freeing the states if the - * check step has failed). - * - * Allocation is performed in the .atomic_check() handler and applied - * automatically when the core swaps the old and new states. - */ - -static bool rcar_du_plane_needs_realloc(struct rcar_du_plane *plane, - struct rcar_du_plane_state *state) -{ - const struct rcar_du_format_info *cur_format; - - cur_format = to_rcar_plane_state(plane->plane.state)->format; - - /* Lowering the number of planes doesn't strictly require reallocation - * as the extra hardware plane will be freed when committing, but doing - * so could lead to more fragmentation. - */ - return !cur_format || cur_format->planes != state->format->planes; -} - -static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state) -{ - unsigned int mask; - - if (state->hwindex == -1) - return 0; - - mask = 1 << state->hwindex; - if (state->format->planes == 2) - mask |= 1 << ((state->hwindex + 1) % 8); - - return mask; -} - -static int rcar_du_plane_hwalloc(unsigned int num_planes, unsigned int free) -{ - unsigned int i; - - for (i = 0; i < RCAR_DU_NUM_HW_PLANES; ++i) { - if (!(free & (1 << i))) - continue; - - if (num_planes == 1 || free & (1 << ((i + 1) % 8))) - break; - } - - return i == RCAR_DU_NUM_HW_PLANES ? -EBUSY : i; -} - static int rcar_du_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { struct rcar_du_device *rcdu = dev->dev_private; - unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, }; - unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, }; - bool needs_realloc = false; - unsigned int groups = 0; - unsigned int i; int ret; ret = drm_atomic_helper_check(dev, state); if (ret < 0) return ret; - /* Check if hardware planes need to be reallocated. */ - for (i = 0; i < dev->mode_config.num_total_plane; ++i) { - struct rcar_du_plane_state *plane_state; - struct rcar_du_plane *plane; - unsigned int index; - - if (!state->planes[i]) - continue; - - plane = to_rcar_plane(state->planes[i]); - plane_state = to_rcar_plane_state(state->plane_states[i]); - - dev_dbg(rcdu->dev, "%s: checking plane (%u,%u)\n", __func__, - plane->group->index, plane - plane->group->planes); - - /* If the plane is being disabled we don't need to go through - * the full reallocation procedure. Just mark the hardware - * plane(s) as freed. - */ - if (!plane_state->format) { - dev_dbg(rcdu->dev, "%s: plane is being disabled\n", - __func__); - index = plane - plane->group->planes; - group_freed_planes[plane->group->index] |= 1 << index; - plane_state->hwindex = -1; - continue; - } - - /* If the plane needs to be reallocated mark it as such, and - * mark the hardware plane(s) as free. - */ - if (rcar_du_plane_needs_realloc(plane, plane_state)) { - dev_dbg(rcdu->dev, "%s: plane needs reallocation\n", - __func__); - groups |= 1 << plane->group->index; - needs_realloc = true; - - index = plane - plane->group->planes; - group_freed_planes[plane->group->index] |= 1 << index; - plane_state->hwindex = -1; - } - } - - if (!needs_realloc) + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) return 0; - /* Grab all plane states for the groups that need reallocation to ensure - * locking and avoid racy updates. This serializes the update operation, - * but there's not much we can do about it as that's the hardware - * design. - * - * Compute the used planes mask for each group at the same time to avoid - * looping over the planes separately later. - */ - while (groups) { - unsigned int index = ffs(groups) - 1; - struct rcar_du_group *group = &rcdu->groups[index]; - unsigned int used_planes = 0; - - dev_dbg(rcdu->dev, "%s: finding free planes for group %u\n", - __func__, index); - - for (i = 0; i < group->num_planes; ++i) { - struct rcar_du_plane *plane = &group->planes[i]; - struct rcar_du_plane_state *plane_state; - struct drm_plane_state *s; - - s = drm_atomic_get_plane_state(state, &plane->plane); - if (IS_ERR(s)) - return PTR_ERR(s); - - /* If the plane has been freed in the above loop its - * hardware planes must not be added to the used planes - * bitmask. However, the current state doesn't reflect - * the free state yet, as we've modified the new state - * above. Use the local freed planes list to check for - * that condition instead. - */ - if (group_freed_planes[index] & (1 << i)) { - dev_dbg(rcdu->dev, - "%s: plane (%u,%u) has been freed, skipping\n", - __func__, plane->group->index, - plane - plane->group->planes); - continue; - } - - plane_state = to_rcar_plane_state(plane->plane.state); - used_planes |= rcar_du_plane_hwmask(plane_state); - - dev_dbg(rcdu->dev, - "%s: plane (%u,%u) uses %u hwplanes (index %d)\n", - __func__, plane->group->index, - plane - plane->group->planes, - plane_state->format ? - plane_state->format->planes : 0, - plane_state->hwindex); - } - - group_free_planes[index] = 0xff & ~used_planes; - groups &= ~(1 << index); - - dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n", - __func__, index, group_free_planes[index]); - } - - /* Reallocate hardware planes for each plane that needs it. */ - for (i = 0; i < dev->mode_config.num_total_plane; ++i) { - struct rcar_du_plane_state *plane_state; - struct rcar_du_plane *plane; - unsigned int crtc_planes; - unsigned int free; - int idx; - - if (!state->planes[i]) - continue; - - plane = to_rcar_plane(state->planes[i]); - plane_state = to_rcar_plane_state(state->plane_states[i]); - - dev_dbg(rcdu->dev, "%s: allocating plane (%u,%u)\n", __func__, - plane->group->index, plane - plane->group->planes); - - /* Skip planes that are being disabled or don't need to be - * reallocated. - */ - if (!plane_state->format || - !rcar_du_plane_needs_realloc(plane, plane_state)) - continue; - - /* Try to allocate the plane from the free planes currently - * associated with the target CRTC to avoid restarting the CRTC - * group and thus minimize flicker. If it fails fall back to - * allocating from all free planes. - */ - crtc_planes = to_rcar_crtc(plane_state->state.crtc)->index % 2 - ? plane->group->dptsr_planes - : ~plane->group->dptsr_planes; - free = group_free_planes[plane->group->index]; - - idx = rcar_du_plane_hwalloc(plane_state->format->planes, - free & crtc_planes); - if (idx < 0) - idx = rcar_du_plane_hwalloc(plane_state->format->planes, - free); - if (idx < 0) { - dev_dbg(rcdu->dev, "%s: no available hardware plane\n", - __func__); - return idx; - } - - dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n", - __func__, plane_state->format->planes, idx); - - plane_state->hwindex = idx; - - group_free_planes[plane->group->index] &= - ~rcar_du_plane_hwmask(plane_state); - - dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n", - __func__, plane->group->index, - group_free_planes[plane->group->index]); - } - - return 0; + return rcar_du_atomic_check_planes(dev, state); } struct rcar_du_commit { @@ -456,7 +257,7 @@ static void rcar_du_atomic_complete(struct rcar_du_commit *commit) /* Apply the atomic update. */ drm_atomic_helper_commit_modeset_disables(dev, old_state); drm_atomic_helper_commit_modeset_enables(dev, old_state); - drm_atomic_helper_commit_planes(dev, old_state, false); + drm_atomic_helper_commit_planes(dev, old_state, true); drm_atomic_helper_wait_for_vblanks(dev, old_state); @@ -775,14 +576,34 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) rgrp->num_crtcs = min(rcdu->num_crtcs - 2 * i, 2U); /* If we have more than one CRTCs in this group pre-associate - * planes 0-3 with CRTC 0 and planes 4-7 with CRTC 1 to minimize - * flicker occurring when the association is changed. + * the low-order planes with CRTC 0 and the high-order planes + * with CRTC 1 to minimize flicker occurring when the + * association is changed. */ - rgrp->dptsr_planes = rgrp->num_crtcs > 1 ? 0xf0 : 0; + rgrp->dptsr_planes = rgrp->num_crtcs > 1 + ? (rcdu->info->gen >= 3 ? 0x04 : 0xf0) + : 0; - ret = rcar_du_planes_init(rgrp); - if (ret < 0) - return ret; + if (!rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) { + ret = rcar_du_planes_init(rgrp); + if (ret < 0) + return ret; + } + } + + /* Initialize the compositors. */ + if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) { + for (i = 0; i < rcdu->num_crtcs; ++i) { + struct rcar_du_vsp *vsp = &rcdu->vsps[i]; + + vsp->index = i; + vsp->dev = rcdu; + rcdu->crtcs[i].vsp = vsp; + + ret = rcar_du_vsp_init(vsp); + if (ret < 0) + return ret; + } } /* Create the CRTCs. */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index 0c43032fc693..e905f5da7aaa 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c @@ -62,12 +62,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { .best_encoder = rcar_du_connector_best_encoder, }; -static void rcar_du_lvds_connector_destroy(struct drm_connector *connector) -{ - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - static enum drm_connector_status rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force) { @@ -79,7 +73,7 @@ static const struct drm_connector_funcs connector_funcs = { .reset = drm_atomic_helper_connector_reset, .detect = rcar_du_lvds_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = rcar_du_lvds_connector_destroy, + .destroy = drm_connector_cleanup, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; @@ -117,9 +111,6 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu, return ret; drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_connector_register(connector); - if (ret < 0) - return ret; connector->dpms = DRM_MODE_DPMS_OFF; drm_object_property_set_value(&connector->base, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c index 85043c5bad03..ef3a50321ecc 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c @@ -38,35 +38,106 @@ static void rcar_lvds_write(struct rcar_du_lvdsenc *lvds, u32 reg, u32 data) iowrite32(data, lvds->mmio + reg); } -static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, - struct rcar_du_crtc *rcrtc) +static void rcar_du_lvdsenc_start_gen2(struct rcar_du_lvdsenc *lvds, + struct rcar_du_crtc *rcrtc) { const struct drm_display_mode *mode = &rcrtc->crtc.mode; unsigned int freq = mode->clock; u32 lvdcr0; - u32 lvdhcr; u32 pllcr; - int ret; - - if (lvds->enabled) - return 0; - - ret = clk_prepare_enable(lvds->clock); - if (ret < 0) - return ret; /* PLL clock configuration */ - if (freq <= 38000) + if (freq < 39000) pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; - else if (freq <= 60000) + else if (freq < 61000) pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; - else if (freq <= 121000) + else if (freq < 121000) pllcr = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; else pllcr = LVDPLLCR_PLLDLYCNT_150M; rcar_lvds_write(lvds, LVDPLLCR, pllcr); + /* Select the input, hardcode mode 0, enable LVDS operation and turn + * bias circuitry on. + */ + lvdcr0 = LVDCR0_BEN | LVDCR0_LVEN; + if (rcrtc->index == 2) + lvdcr0 |= LVDCR0_DUSEL; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + /* Turn all the channels on. */ + rcar_lvds_write(lvds, LVDCR1, + LVDCR1_CHSTBY_GEN2(3) | LVDCR1_CHSTBY_GEN2(2) | + LVDCR1_CHSTBY_GEN2(1) | LVDCR1_CHSTBY_GEN2(0) | + LVDCR1_CLKSTBY_GEN2); + + /* Turn the PLL on, wait for the startup delay, and turn the output + * on. + */ + lvdcr0 |= LVDCR0_PLLON; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + usleep_range(100, 150); + + lvdcr0 |= LVDCR0_LVRES; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); +} + +static void rcar_du_lvdsenc_start_gen3(struct rcar_du_lvdsenc *lvds, + struct rcar_du_crtc *rcrtc) +{ + const struct drm_display_mode *mode = &rcrtc->crtc.mode; + unsigned int freq = mode->clock; + u32 lvdcr0; + u32 pllcr; + + /* PLL clock configuration */ + if (freq < 42000) + pllcr = LVDPLLCR_PLLDIVCNT_42M; + else if (freq < 85000) + pllcr = LVDPLLCR_PLLDIVCNT_85M; + else if (freq < 128000) + pllcr = LVDPLLCR_PLLDIVCNT_128M; + else + pllcr = LVDPLLCR_PLLDIVCNT_148M; + + rcar_lvds_write(lvds, LVDPLLCR, pllcr); + + /* Turn the PLL on, set it to LVDS normal mode, wait for the startup + * delay and turn the output on. + */ + lvdcr0 = LVDCR0_PLLON; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + lvdcr0 |= LVDCR0_PWD; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + usleep_range(100, 150); + + lvdcr0 |= LVDCR0_LVRES; + rcar_lvds_write(lvds, LVDCR0, lvdcr0); + + /* Turn all the channels on. */ + rcar_lvds_write(lvds, LVDCR1, + LVDCR1_CHSTBY_GEN3(3) | LVDCR1_CHSTBY_GEN3(2) | + LVDCR1_CHSTBY_GEN3(1) | LVDCR1_CHSTBY_GEN3(0) | + LVDCR1_CLKSTBY_GEN3); +} + +static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, + struct rcar_du_crtc *rcrtc) +{ + u32 lvdhcr; + int ret; + + if (lvds->enabled) + return 0; + + ret = clk_prepare_enable(lvds->clock); + if (ret < 0) + return ret; + /* Hardcode the channels and control signals routing for now. * * HSYNC -> CTRL0 @@ -87,30 +158,14 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds, rcar_lvds_write(lvds, LVDCHCR, lvdhcr); - /* Select the input, hardcode mode 0, enable LVDS operation and turn - * bias circuitry on. - */ - lvdcr0 = LVDCR0_BEN | LVDCR0_LVEN; - if (rcrtc->index == 2) - lvdcr0 |= LVDCR0_DUSEL; - rcar_lvds_write(lvds, LVDCR0, lvdcr0); - - /* Turn all the channels on. */ - rcar_lvds_write(lvds, LVDCR1, LVDCR1_CHSTBY(3) | LVDCR1_CHSTBY(2) | - LVDCR1_CHSTBY(1) | LVDCR1_CHSTBY(0) | LVDCR1_CLKSTBY); - - /* Turn the PLL on, wait for the startup delay, and turn the output - * on. - */ - lvdcr0 |= LVDCR0_PLLEN; - rcar_lvds_write(lvds, LVDCR0, lvdcr0); - - usleep_range(100, 150); - - lvdcr0 |= LVDCR0_LVRES; - rcar_lvds_write(lvds, LVDCR0, lvdcr0); + /* Perform generation-specific initialization. */ + if (lvds->dev->info->gen < 3) + rcar_du_lvdsenc_start_gen2(lvds, rcrtc); + else + rcar_du_lvdsenc_start_gen3(lvds, rcrtc); lvds->enabled = true; + return 0; } @@ -140,6 +195,21 @@ int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, struct drm_crtc *crtc, return -EINVAL; } +void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds, + struct drm_display_mode *mode) +{ + struct rcar_du_device *rcdu = lvds->dev; + + /* The internal LVDS encoder has a restricted clock frequency operating + * range (30MHz to 150MHz on Gen2, 25.175MHz to 148.5MHz on Gen3). Clamp + * the clock accordingly. + */ + if (rcdu->info->gen < 3) + mode->clock = clamp(mode->clock, 30000, 150000); + else + mode->clock = clamp(mode->clock, 25175, 148500); +} + static int rcar_du_lvdsenc_get_resources(struct rcar_du_lvdsenc *lvds, struct platform_device *pdev) { diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h index 9a6001c07303..dfdba746edf4 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h @@ -30,6 +30,8 @@ enum rcar_lvds_input { int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu); int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, struct drm_crtc *crtc, bool enable); +void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds, + struct drm_display_mode *mode); #else static inline int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu) { @@ -40,6 +42,10 @@ static inline int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, { return 0; } +static inline void rcar_du_lvdsenc_atomic_check(struct rcar_du_lvdsenc *lvds, + struct drm_display_mode *mode) +{ +} #endif #endif /* __RCAR_DU_LVDSENC_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index c3ed9522c0e1..8460ae1ffa4b 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -1,7 +1,7 @@ /* * rcar_du_plane.c -- R-Car Display Unit Planes * - * Copyright (C) 2013-2014 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -12,6 +12,7 @@ */ #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> @@ -20,21 +21,300 @@ #include <drm/drm_plane_helper.h> #include "rcar_du_drv.h" +#include "rcar_du_group.h" #include "rcar_du_kms.h" #include "rcar_du_plane.h" #include "rcar_du_regs.h" -#define RCAR_DU_COLORKEY_NONE (0 << 24) -#define RCAR_DU_COLORKEY_SOURCE (1 << 24) -#define RCAR_DU_COLORKEY_MASK (1 << 24) +/* ----------------------------------------------------------------------------- + * Atomic hardware plane allocator + * + * The hardware plane allocator is solely based on the atomic plane states + * without keeping any external state to avoid races between .atomic_check() + * and .atomic_commit(). + * + * The core idea is to avoid using a free planes bitmask that would need to be + * shared between check and commit handlers with a collective knowledge based on + * the allocated hardware plane(s) for each KMS plane. The allocator then loops + * over all plane states to compute the free planes bitmask, allocates hardware + * planes based on that bitmask, and stores the result back in the plane states. + * + * For this to work we need to access the current state of planes not touched by + * the atomic update. To ensure that it won't be modified, we need to lock all + * planes using drm_atomic_get_plane_state(). This effectively serializes atomic + * updates from .atomic_check() up to completion (when swapping the states if + * the check step has succeeded) or rollback (when freeing the states if the + * check step has failed). + * + * Allocation is performed in the .atomic_check() handler and applied + * automatically when the core swaps the old and new states. + */ + +static bool rcar_du_plane_needs_realloc(struct rcar_du_plane *plane, + struct rcar_du_plane_state *new_state) +{ + struct rcar_du_plane_state *cur_state; + + cur_state = to_rcar_plane_state(plane->plane.state); + + /* Lowering the number of planes doesn't strictly require reallocation + * as the extra hardware plane will be freed when committing, but doing + * so could lead to more fragmentation. + */ + if (!cur_state->format || + cur_state->format->planes != new_state->format->planes) + return true; + + /* Reallocate hardware planes if the source has changed. */ + if (cur_state->source != new_state->source) + return true; -static u32 rcar_du_plane_read(struct rcar_du_group *rgrp, - unsigned int index, u32 reg) + return false; +} + +static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state) +{ + unsigned int mask; + + if (state->hwindex == -1) + return 0; + + mask = 1 << state->hwindex; + if (state->format->planes == 2) + mask |= 1 << ((state->hwindex + 1) % 8); + + return mask; +} + +/* + * The R8A7790 DU can source frames directly from the VSP1 devices VSPD0 and + * VSPD1. VSPD0 feeds DU0/1 plane 0, and VSPD1 feeds either DU2 plane 0 or + * DU0/1 plane 1. + * + * Allocate the correct fixed plane when sourcing frames from VSPD0 or VSPD1, + * and allocate planes in reverse index order otherwise to ensure maximum + * availability of planes 0 and 1. + * + * The caller is responsible for ensuring that the requested source is + * compatible with the DU revision. + */ +static int rcar_du_plane_hwalloc(struct rcar_du_plane *plane, + struct rcar_du_plane_state *state, + unsigned int free) { - return rcar_du_read(rgrp->dev, - rgrp->mmio_offset + index * PLANE_OFF + reg); + unsigned int num_planes = state->format->planes; + int fixed = -1; + int i; + + if (state->source == RCAR_DU_PLANE_VSPD0) { + /* VSPD0 feeds plane 0 on DU0/1. */ + if (plane->group->index != 0) + return -EINVAL; + + fixed = 0; + } else if (state->source == RCAR_DU_PLANE_VSPD1) { + /* VSPD1 feeds plane 1 on DU0/1 or plane 0 on DU2. */ + fixed = plane->group->index == 0 ? 1 : 0; + } + + if (fixed >= 0) + return free & (1 << fixed) ? fixed : -EBUSY; + + for (i = RCAR_DU_NUM_HW_PLANES - 1; i >= 0; --i) { + if (!(free & (1 << i))) + continue; + + if (num_planes == 1 || free & (1 << ((i + 1) % 8))) + break; + } + + return i < 0 ? -EBUSY : i; } +int rcar_du_atomic_check_planes(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct rcar_du_device *rcdu = dev->dev_private; + unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, }; + unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, }; + bool needs_realloc = false; + unsigned int groups = 0; + unsigned int i; + + /* Check if hardware planes need to be reallocated. */ + for (i = 0; i < dev->mode_config.num_total_plane; ++i) { + struct rcar_du_plane_state *plane_state; + struct rcar_du_plane *plane; + unsigned int index; + + if (!state->planes[i]) + continue; + + plane = to_rcar_plane(state->planes[i]); + plane_state = to_rcar_plane_state(state->plane_states[i]); + + dev_dbg(rcdu->dev, "%s: checking plane (%u,%tu)\n", __func__, + plane->group->index, plane - plane->group->planes); + + /* If the plane is being disabled we don't need to go through + * the full reallocation procedure. Just mark the hardware + * plane(s) as freed. + */ + if (!plane_state->format) { + dev_dbg(rcdu->dev, "%s: plane is being disabled\n", + __func__); + index = plane - plane->group->planes; + group_freed_planes[plane->group->index] |= 1 << index; + plane_state->hwindex = -1; + continue; + } + + /* If the plane needs to be reallocated mark it as such, and + * mark the hardware plane(s) as free. + */ + if (rcar_du_plane_needs_realloc(plane, plane_state)) { + dev_dbg(rcdu->dev, "%s: plane needs reallocation\n", + __func__); + groups |= 1 << plane->group->index; + needs_realloc = true; + + index = plane - plane->group->planes; + group_freed_planes[plane->group->index] |= 1 << index; + plane_state->hwindex = -1; + } + } + + if (!needs_realloc) + return 0; + + /* Grab all plane states for the groups that need reallocation to ensure + * locking and avoid racy updates. This serializes the update operation, + * but there's not much we can do about it as that's the hardware + * design. + * + * Compute the used planes mask for each group at the same time to avoid + * looping over the planes separately later. + */ + while (groups) { + unsigned int index = ffs(groups) - 1; + struct rcar_du_group *group = &rcdu->groups[index]; + unsigned int used_planes = 0; + + dev_dbg(rcdu->dev, "%s: finding free planes for group %u\n", + __func__, index); + + for (i = 0; i < group->num_planes; ++i) { + struct rcar_du_plane *plane = &group->planes[i]; + struct rcar_du_plane_state *plane_state; + struct drm_plane_state *s; + + s = drm_atomic_get_plane_state(state, &plane->plane); + if (IS_ERR(s)) + return PTR_ERR(s); + + /* If the plane has been freed in the above loop its + * hardware planes must not be added to the used planes + * bitmask. However, the current state doesn't reflect + * the free state yet, as we've modified the new state + * above. Use the local freed planes list to check for + * that condition instead. + */ + if (group_freed_planes[index] & (1 << i)) { + dev_dbg(rcdu->dev, + "%s: plane (%u,%tu) has been freed, skipping\n", + __func__, plane->group->index, + plane - plane->group->planes); + continue; + } + + plane_state = to_rcar_plane_state(plane->plane.state); + used_planes |= rcar_du_plane_hwmask(plane_state); + + dev_dbg(rcdu->dev, + "%s: plane (%u,%tu) uses %u hwplanes (index %d)\n", + __func__, plane->group->index, + plane - plane->group->planes, + plane_state->format ? + plane_state->format->planes : 0, + plane_state->hwindex); + } + + group_free_planes[index] = 0xff & ~used_planes; + groups &= ~(1 << index); + + dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n", + __func__, index, group_free_planes[index]); + } + + /* Reallocate hardware planes for each plane that needs it. */ + for (i = 0; i < dev->mode_config.num_total_plane; ++i) { + struct rcar_du_plane_state *plane_state; + struct rcar_du_plane *plane; + unsigned int crtc_planes; + unsigned int free; + int idx; + + if (!state->planes[i]) + continue; + + plane = to_rcar_plane(state->planes[i]); + plane_state = to_rcar_plane_state(state->plane_states[i]); + + dev_dbg(rcdu->dev, "%s: allocating plane (%u,%tu)\n", __func__, + plane->group->index, plane - plane->group->planes); + + /* Skip planes that are being disabled or don't need to be + * reallocated. + */ + if (!plane_state->format || + !rcar_du_plane_needs_realloc(plane, plane_state)) + continue; + + /* Try to allocate the plane from the free planes currently + * associated with the target CRTC to avoid restarting the CRTC + * group and thus minimize flicker. If it fails fall back to + * allocating from all free planes. + */ + crtc_planes = to_rcar_crtc(plane_state->state.crtc)->index % 2 + ? plane->group->dptsr_planes + : ~plane->group->dptsr_planes; + free = group_free_planes[plane->group->index]; + + idx = rcar_du_plane_hwalloc(plane, plane_state, + free & crtc_planes); + if (idx < 0) + idx = rcar_du_plane_hwalloc(plane, plane_state, + free); + if (idx < 0) { + dev_dbg(rcdu->dev, "%s: no available hardware plane\n", + __func__); + return idx; + } + + dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n", + __func__, plane_state->format->planes, idx); + + plane_state->hwindex = idx; + + group_free_planes[plane->group->index] &= + ~rcar_du_plane_hwmask(plane_state); + + dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n", + __func__, plane->group->index, + group_free_planes[plane->group->index]); + } + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Plane Setup + */ + +#define RCAR_DU_COLORKEY_NONE (0 << 24) +#define RCAR_DU_COLORKEY_SOURCE (1 << 24) +#define RCAR_DU_COLORKEY_MASK (1 << 24) + static void rcar_du_plane_write(struct rcar_du_group *rgrp, unsigned int index, u32 reg, u32 data) { @@ -42,34 +322,45 @@ static void rcar_du_plane_write(struct rcar_du_group *rgrp, data); } -static void rcar_du_plane_setup_fb(struct rcar_du_plane *plane) +static void rcar_du_plane_setup_scanout(struct rcar_du_group *rgrp, + const struct rcar_du_plane_state *state) { - struct rcar_du_plane_state *state = - to_rcar_plane_state(plane->plane.state); - struct drm_framebuffer *fb = plane->plane.state->fb; - struct rcar_du_group *rgrp = plane->group; unsigned int src_x = state->state.src_x >> 16; unsigned int src_y = state->state.src_y >> 16; unsigned int index = state->hwindex; - struct drm_gem_cma_object *gem; + unsigned int pitch; bool interlaced; - u32 mwr; + u32 dma[2]; interlaced = state->state.crtc->state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE; + if (state->source == RCAR_DU_PLANE_MEMORY) { + struct drm_framebuffer *fb = state->state.fb; + struct drm_gem_cma_object *gem; + unsigned int i; + + if (state->format->planes == 2) + pitch = fb->pitches[0]; + else + pitch = fb->pitches[0] * 8 / state->format->bpp; + + for (i = 0; i < state->format->planes; ++i) { + gem = drm_fb_cma_get_gem_obj(fb, i); + dma[i] = gem->paddr + fb->offsets[i]; + } + } else { + pitch = state->state.src_w >> 16; + dma[0] = 0; + dma[1] = 0; + } + /* Memory pitch (expressed in pixels). Must be doubled for interlaced * operation with 32bpp formats. */ - if (state->format->planes == 2) - mwr = fb->pitches[0]; - else - mwr = fb->pitches[0] * 8 / state->format->bpp; - - if (interlaced && state->format->bpp == 32) - mwr *= 2; - - rcar_du_plane_write(rgrp, index, PnMWR, mwr); + rcar_du_plane_write(rgrp, index, PnMWR, + (interlaced && state->format->bpp == 32) ? + pitch * 2 : pitch); /* The Y position is expressed in raster line units and must be doubled * for 32bpp formats, according to the R8A7790 datasheet. No mention of @@ -87,30 +378,25 @@ static void rcar_du_plane_setup_fb(struct rcar_du_plane *plane) rcar_du_plane_write(rgrp, index, PnSPYR, src_y * (!interlaced && state->format->bpp == 32 ? 2 : 1)); - gem = drm_fb_cma_get_gem_obj(fb, 0); - rcar_du_plane_write(rgrp, index, PnDSA0R, gem->paddr + fb->offsets[0]); + rcar_du_plane_write(rgrp, index, PnDSA0R, dma[0]); if (state->format->planes == 2) { index = (index + 1) % 8; - rcar_du_plane_write(rgrp, index, PnMWR, fb->pitches[0]); + rcar_du_plane_write(rgrp, index, PnMWR, pitch); rcar_du_plane_write(rgrp, index, PnSPXR, src_x); rcar_du_plane_write(rgrp, index, PnSPYR, src_y * (state->format->bpp == 16 ? 2 : 1) / 2); - gem = drm_fb_cma_get_gem_obj(fb, 1); - rcar_du_plane_write(rgrp, index, PnDSA0R, - gem->paddr + fb->offsets[1]); + rcar_du_plane_write(rgrp, index, PnDSA0R, dma[1]); } } -static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, - unsigned int index) +static void rcar_du_plane_setup_mode(struct rcar_du_group *rgrp, + unsigned int index, + const struct rcar_du_plane_state *state) { - struct rcar_du_plane_state *state = - to_rcar_plane_state(plane->plane.state); - struct rcar_du_group *rgrp = plane->group; u32 colorkey; u32 pnmr; @@ -168,12 +454,10 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane, } } -static void __rcar_du_plane_setup(struct rcar_du_plane *plane, - unsigned int index) +static void rcar_du_plane_setup_format_gen2(struct rcar_du_group *rgrp, + unsigned int index, + const struct rcar_du_plane_state *state) { - struct rcar_du_plane_state *state = - to_rcar_plane_state(plane->plane.state); - struct rcar_du_group *rgrp = plane->group; u32 ddcr2 = PnDDCR2_CODE; u32 ddcr4; @@ -182,11 +466,8 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, * The data format is selected by the DDDF field in PnMR and the EDF * field in DDCR4. */ - ddcr4 = rcar_du_plane_read(rgrp, index, PnDDCR4); - ddcr4 &= ~PnDDCR4_EDF_MASK; - ddcr4 |= state->format->edf | PnDDCR4_CODE; - rcar_du_plane_setup_mode(plane, index); + rcar_du_plane_setup_mode(rgrp, index, state); if (state->format->planes == 2) { if (state->hwindex != index) { @@ -204,31 +485,72 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane, } rcar_du_plane_write(rgrp, index, PnDDCR2, ddcr2); + + ddcr4 = state->format->edf | PnDDCR4_CODE; + if (state->source != RCAR_DU_PLANE_MEMORY) + ddcr4 |= PnDDCR4_VSPS; + rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4); +} + +static void rcar_du_plane_setup_format_gen3(struct rcar_du_group *rgrp, + unsigned int index, + const struct rcar_du_plane_state *state) +{ + rcar_du_plane_write(rgrp, index, PnMR, + PnMR_SPIM_TP_OFF | state->format->pnmr); + + rcar_du_plane_write(rgrp, index, PnDDCR4, + state->format->edf | PnDDCR4_CODE); +} + +static void rcar_du_plane_setup_format(struct rcar_du_group *rgrp, + unsigned int index, + const struct rcar_du_plane_state *state) +{ + struct rcar_du_device *rcdu = rgrp->dev; + + if (rcdu->info->gen < 3) + rcar_du_plane_setup_format_gen2(rgrp, index, state); + else + rcar_du_plane_setup_format_gen3(rgrp, index, state); /* Destination position and size */ - rcar_du_plane_write(rgrp, index, PnDSXR, plane->plane.state->crtc_w); - rcar_du_plane_write(rgrp, index, PnDSYR, plane->plane.state->crtc_h); - rcar_du_plane_write(rgrp, index, PnDPXR, plane->plane.state->crtc_x); - rcar_du_plane_write(rgrp, index, PnDPYR, plane->plane.state->crtc_y); - - /* Wrap-around and blinking, disabled */ - rcar_du_plane_write(rgrp, index, PnWASPR, 0); - rcar_du_plane_write(rgrp, index, PnWAMWR, 4095); - rcar_du_plane_write(rgrp, index, PnBTR, 0); - rcar_du_plane_write(rgrp, index, PnMLR, 0); + rcar_du_plane_write(rgrp, index, PnDSXR, state->state.crtc_w); + rcar_du_plane_write(rgrp, index, PnDSYR, state->state.crtc_h); + rcar_du_plane_write(rgrp, index, PnDPXR, state->state.crtc_x); + rcar_du_plane_write(rgrp, index, PnDPYR, state->state.crtc_y); + + if (rcdu->info->gen < 3) { + /* Wrap-around and blinking, disabled */ + rcar_du_plane_write(rgrp, index, PnWASPR, 0); + rcar_du_plane_write(rgrp, index, PnWAMWR, 4095); + rcar_du_plane_write(rgrp, index, PnBTR, 0); + rcar_du_plane_write(rgrp, index, PnMLR, 0); + } } -void rcar_du_plane_setup(struct rcar_du_plane *plane) +void __rcar_du_plane_setup(struct rcar_du_group *rgrp, + const struct rcar_du_plane_state *state) { - struct rcar_du_plane_state *state = - to_rcar_plane_state(plane->plane.state); + struct rcar_du_device *rcdu = rgrp->dev; - __rcar_du_plane_setup(plane, state->hwindex); + rcar_du_plane_setup_format(rgrp, state->hwindex, state); if (state->format->planes == 2) - __rcar_du_plane_setup(plane, (state->hwindex + 1) % 8); + rcar_du_plane_setup_format(rgrp, (state->hwindex + 1) % 8, + state); - rcar_du_plane_setup_fb(plane); + if (rcdu->info->gen < 3) + rcar_du_plane_setup_scanout(rgrp, state); + + if (state->source == RCAR_DU_PLANE_VSPD1) { + unsigned int vspd1_sink = rgrp->index ? 2 : 0; + + if (rcdu->vspd1_sink != vspd1_sink) { + rcdu->vspd1_sink = vspd1_sink; + rcar_du_set_dpad0_vsp1_routing(rcdu); + } + } } static int rcar_du_plane_atomic_check(struct drm_plane *plane, @@ -263,9 +585,27 @@ static void rcar_du_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { struct rcar_du_plane *rplane = to_rcar_plane(plane); + struct rcar_du_plane_state *old_rstate; + struct rcar_du_plane_state *new_rstate; + + if (!plane->state->crtc) + return; + + rcar_du_plane_setup(rplane); + + /* Check whether the source has changed from memory to live source or + * from live source to memory. The source has been configured by the + * VSPS bit in the PnDDCR4 register. Although the datasheet states that + * the bit is updated during vertical blanking, it seems that updates + * only occur when the DU group is held in reset through the DSYSR.DRES + * bit. We thus need to restart the group if the source changes. + */ + old_rstate = to_rcar_plane_state(old_state); + new_rstate = to_rcar_plane_state(plane->state); - if (plane->state->crtc) - rcar_du_plane_setup(rplane); + if ((old_rstate->source == RCAR_DU_PLANE_MEMORY) != + (new_rstate->source == RCAR_DU_PLANE_MEMORY)) + rplane->group->need_restart = true; } static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = { @@ -313,6 +653,7 @@ static void rcar_du_plane_reset(struct drm_plane *plane) return; state->hwindex = -1; + state->source = RCAR_DU_PLANE_MEMORY; state->alpha = 255; state->colorkey = RCAR_DU_COLORKEY_NONE; state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.h b/drivers/gpu/drm/rcar-du/rcar_du_plane.h index 9732bff1911b..b18b7b25dbfa 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.h @@ -28,6 +28,12 @@ struct rcar_du_group; #define RCAR_DU_NUM_KMS_PLANES 9 #define RCAR_DU_NUM_HW_PLANES 8 +enum rcar_du_plane_source { + RCAR_DU_PLANE_MEMORY, + RCAR_DU_PLANE_VSPD0, + RCAR_DU_PLANE_VSPD1, +}; + struct rcar_du_plane { struct drm_plane plane; struct rcar_du_group *group; @@ -52,6 +58,7 @@ struct rcar_du_plane_state { const struct rcar_du_format_info *format; int hwindex; + enum rcar_du_plane_source source; unsigned int alpha; unsigned int colorkey; @@ -64,8 +71,20 @@ to_rcar_plane_state(struct drm_plane_state *state) return container_of(state, struct rcar_du_plane_state, state); } +int rcar_du_atomic_check_planes(struct drm_device *dev, + struct drm_atomic_state *state); + int rcar_du_planes_init(struct rcar_du_group *rgrp); -void rcar_du_plane_setup(struct rcar_du_plane *plane); +void __rcar_du_plane_setup(struct rcar_du_group *rgrp, + const struct rcar_du_plane_state *state); + +static inline void rcar_du_plane_setup(struct rcar_du_plane *plane) +{ + struct rcar_du_plane_state *state = + to_rcar_plane_state(plane->plane.state); + + return __rcar_du_plane_setup(plane->group, state); +} #endif /* __RCAR_DU_PLANE_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h index 70fcbc471ebd..d2f66068e52c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h @@ -1,7 +1,7 @@ /* * rcar_du_regs.h -- R-Car Display Unit Registers Definitions * - * Copyright (C) 2013 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -16,6 +16,7 @@ #define DU0_REG_OFFSET 0x00000 #define DU1_REG_OFFSET 0x30000 #define DU2_REG_OFFSET 0x40000 +#define DU3_REG_OFFSET 0x70000 /* ----------------------------------------------------------------------------- * Display Control Registers @@ -186,7 +187,7 @@ #define DEFR6 0x000e8 #define DEFR6_CODE (0x7778 << 16) -#define DEFR6_ODPM22_D2SMR (0 << 10) +#define DEFR6_ODPM22_DSMR (0 << 10) #define DEFR6_ODPM22_DISP (2 << 10) #define DEFR6_ODPM22_CDE (3 << 10) #define DEFR6_ODPM22_MASK (3 << 10) @@ -260,6 +261,21 @@ #define DIDSR_PDCS_CLK(n, clk) (clk << ((n) * 2)) #define DIDSR_PDCS_MASK(n) (3 << ((n) * 2)) +#define DEFR10 0x20038 +#define DEFR10_CODE (0x7795 << 16) +#define DEFR10_VSPF1_RGB (0 << 14) +#define DEFR10_VSPF1_YC (1 << 14) +#define DEFR10_DOCF1_RGB (0 << 12) +#define DEFR10_DOCF1_YC (1 << 12) +#define DEFR10_YCDF0_YCBCR444 (0 << 11) +#define DEFR10_YCDF0_YCBCR422 (1 << 11) +#define DEFR10_VSPF0_RGB (0 << 10) +#define DEFR10_VSPF0_YC (1 << 10) +#define DEFR10_DOCF0_RGB (0 << 8) +#define DEFR10_DOCF0_YC (1 << 8) +#define DEFR10_TSEL_H3_TCON1 (0 << 1) /* DEFR102 register only (DU2/DU3) */ +#define DEFR10_DEFE10 (1 << 0) + /* ----------------------------------------------------------------------------- * Display Timing Generation Registers */ @@ -389,6 +405,7 @@ #define PnDDCR4 0x00190 #define PnDDCR4_CODE (0x7766 << 16) +#define PnDDCR4_VSPS (1 << 13) #define PnDDCR4_SDFS_RGB (0 << 4) #define PnDDCR4_SDFS_YC (5 << 4) #define PnDDCR4_SDFS_MASK (7 << 4) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index e0a5d8f93963..9d7e5c99caf6 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c @@ -31,12 +31,6 @@ static const struct drm_connector_helper_funcs connector_helper_funcs = { .best_encoder = rcar_du_connector_best_encoder, }; -static void rcar_du_vga_connector_destroy(struct drm_connector *connector) -{ - drm_connector_unregister(connector); - drm_connector_cleanup(connector); -} - static enum drm_connector_status rcar_du_vga_connector_detect(struct drm_connector *connector, bool force) { @@ -48,7 +42,7 @@ static const struct drm_connector_funcs connector_funcs = { .reset = drm_atomic_helper_connector_reset, .detect = rcar_du_vga_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = rcar_du_vga_connector_destroy, + .destroy = drm_connector_cleanup, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; @@ -76,9 +70,6 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu, return ret; drm_connector_helper_add(connector, &connector_helper_funcs); - ret = drm_connector_register(connector); - if (ret < 0) - return ret; connector->dpms = DRM_MODE_DPMS_OFF; drm_object_property_set_value(&connector->base, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c new file mode 100644 index 000000000000..de7ef041182b --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.c @@ -0,0 +1,384 @@ +/* + * rcar_du_vsp.h -- R-Car Display Unit VSP-Based Compositor + * + * Copyright (C) 2015 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_plane_helper.h> + +#include <linux/of_platform.h> +#include <linux/videodev2.h> + +#include <media/vsp1.h> + +#include "rcar_du_drv.h" +#include "rcar_du_kms.h" +#include "rcar_du_vsp.h" + +void rcar_du_vsp_enable(struct rcar_du_crtc *crtc) +{ + const struct drm_display_mode *mode = &crtc->crtc.state->adjusted_mode; + struct rcar_du_device *rcdu = crtc->group->dev; + struct rcar_du_plane_state state = { + .state = { + .crtc = &crtc->crtc, + .crtc_x = 0, + .crtc_y = 0, + .crtc_w = mode->hdisplay, + .crtc_h = mode->vdisplay, + .src_x = 0, + .src_y = 0, + .src_w = mode->hdisplay << 16, + .src_h = mode->vdisplay << 16, + }, + .format = rcar_du_format_info(DRM_FORMAT_ARGB8888), + .source = RCAR_DU_PLANE_VSPD1, + .alpha = 255, + .colorkey = 0, + .zpos = 0, + }; + + if (rcdu->info->gen >= 3) + state.hwindex = (crtc->index % 2) ? 2 : 0; + else + state.hwindex = crtc->index % 2; + + __rcar_du_plane_setup(crtc->group, &state); + + /* Ensure that the plane source configuration takes effect by requesting + * a restart of the group. See rcar_du_plane_atomic_update() for a more + * detailed explanation. + * + * TODO: Check whether this is still needed on Gen3. + */ + crtc->group->need_restart = true; + + vsp1_du_setup_lif(crtc->vsp->vsp, mode->hdisplay, mode->vdisplay); +} + +void rcar_du_vsp_disable(struct rcar_du_crtc *crtc) +{ + vsp1_du_setup_lif(crtc->vsp->vsp, 0, 0); +} + +void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc) +{ + vsp1_du_atomic_begin(crtc->vsp->vsp); +} + +void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc) +{ + vsp1_du_atomic_flush(crtc->vsp->vsp); +} + +/* Keep the two tables in sync. */ +static const u32 formats_kms[] = { + DRM_FORMAT_RGB332, + DRM_FORMAT_ARGB4444, + DRM_FORMAT_XRGB4444, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR888, + DRM_FORMAT_RGB888, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_NV12, + DRM_FORMAT_NV21, + DRM_FORMAT_NV16, + DRM_FORMAT_NV61, + DRM_FORMAT_YUV420, + DRM_FORMAT_YVU420, + DRM_FORMAT_YUV422, + DRM_FORMAT_YVU422, + DRM_FORMAT_YUV444, + DRM_FORMAT_YVU444, +}; + +static const u32 formats_v4l2[] = { + V4L2_PIX_FMT_RGB332, + V4L2_PIX_FMT_ARGB444, + V4L2_PIX_FMT_XRGB444, + V4L2_PIX_FMT_ARGB555, + V4L2_PIX_FMT_XRGB555, + V4L2_PIX_FMT_RGB565, + V4L2_PIX_FMT_RGB24, + V4L2_PIX_FMT_BGR24, + V4L2_PIX_FMT_ARGB32, + V4L2_PIX_FMT_XRGB32, + V4L2_PIX_FMT_ABGR32, + V4L2_PIX_FMT_XBGR32, + V4L2_PIX_FMT_UYVY, + V4L2_PIX_FMT_VYUY, + V4L2_PIX_FMT_YUYV, + V4L2_PIX_FMT_YVYU, + V4L2_PIX_FMT_NV12M, + V4L2_PIX_FMT_NV21M, + V4L2_PIX_FMT_NV16M, + V4L2_PIX_FMT_NV61M, + V4L2_PIX_FMT_YUV420M, + V4L2_PIX_FMT_YVU420M, + V4L2_PIX_FMT_YUV422M, + V4L2_PIX_FMT_YVU422M, + V4L2_PIX_FMT_YUV444M, + V4L2_PIX_FMT_YVU444M, +}; + +static void rcar_du_vsp_plane_setup(struct rcar_du_vsp_plane *plane) +{ + struct rcar_du_vsp_plane_state *state = + to_rcar_vsp_plane_state(plane->plane.state); + struct drm_framebuffer *fb = plane->plane.state->fb; + struct v4l2_rect src; + struct v4l2_rect dst; + dma_addr_t paddr[2] = { 0, }; + u32 pixelformat = 0; + unsigned int i; + + src.left = state->state.src_x >> 16; + src.top = state->state.src_y >> 16; + src.width = state->state.src_w >> 16; + src.height = state->state.src_h >> 16; + + dst.left = state->state.crtc_x; + dst.top = state->state.crtc_y; + dst.width = state->state.crtc_w; + dst.height = state->state.crtc_h; + + for (i = 0; i < state->format->planes; ++i) { + struct drm_gem_cma_object *gem; + + gem = drm_fb_cma_get_gem_obj(fb, i); + paddr[i] = gem->paddr + fb->offsets[i]; + } + + for (i = 0; i < ARRAY_SIZE(formats_kms); ++i) { + if (formats_kms[i] == state->format->fourcc) { + pixelformat = formats_v4l2[i]; + break; + } + } + + WARN_ON(!pixelformat); + + vsp1_du_atomic_update(plane->vsp->vsp, plane->index, pixelformat, + fb->pitches[0], paddr, &src, &dst); +} + +static int rcar_du_vsp_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state); + struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane); + struct rcar_du_device *rcdu = rplane->vsp->dev; + + if (!state->fb || !state->crtc) { + rstate->format = NULL; + return 0; + } + + if (state->src_w >> 16 != state->crtc_w || + state->src_h >> 16 != state->crtc_h) { + dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__); + return -EINVAL; + } + + rstate->format = rcar_du_format_info(state->fb->pixel_format); + if (rstate->format == NULL) { + dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__, + state->fb->pixel_format); + return -EINVAL; + } + + return 0; +} + +static void rcar_du_vsp_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct rcar_du_vsp_plane *rplane = to_rcar_vsp_plane(plane); + + if (plane->state->crtc) + rcar_du_vsp_plane_setup(rplane); + else + vsp1_du_atomic_update(rplane->vsp->vsp, rplane->index, 0, 0, 0, + NULL, NULL); +} + +static const struct drm_plane_helper_funcs rcar_du_vsp_plane_helper_funcs = { + .atomic_check = rcar_du_vsp_plane_atomic_check, + .atomic_update = rcar_du_vsp_plane_atomic_update, +}; + +static struct drm_plane_state * +rcar_du_vsp_plane_atomic_duplicate_state(struct drm_plane *plane) +{ + struct rcar_du_vsp_plane_state *state; + struct rcar_du_vsp_plane_state *copy; + + if (WARN_ON(!plane->state)) + return NULL; + + state = to_rcar_vsp_plane_state(plane->state); + copy = kmemdup(state, sizeof(*state), GFP_KERNEL); + if (copy == NULL) + return NULL; + + __drm_atomic_helper_plane_duplicate_state(plane, ©->state); + + return ©->state; +} + +static void rcar_du_vsp_plane_atomic_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + __drm_atomic_helper_plane_destroy_state(plane, state); + kfree(to_rcar_vsp_plane_state(state)); +} + +static void rcar_du_vsp_plane_reset(struct drm_plane *plane) +{ + struct rcar_du_vsp_plane_state *state; + + if (plane->state) { + rcar_du_vsp_plane_atomic_destroy_state(plane, plane->state); + plane->state = NULL; + } + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state == NULL) + return; + + state->alpha = 255; + + plane->state = &state->state; + plane->state->plane = plane; +} + +static int rcar_du_vsp_plane_atomic_set_property(struct drm_plane *plane, + struct drm_plane_state *state, struct drm_property *property, + uint64_t val) +{ + struct rcar_du_vsp_plane_state *rstate = to_rcar_vsp_plane_state(state); + struct rcar_du_device *rcdu = to_rcar_vsp_plane(plane)->vsp->dev; + + if (property == rcdu->props.alpha) + rstate->alpha = val; + else + return -EINVAL; + + return 0; +} + +static int rcar_du_vsp_plane_atomic_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, struct drm_property *property, + uint64_t *val) +{ + const struct rcar_du_vsp_plane_state *rstate = + container_of(state, const struct rcar_du_vsp_plane_state, state); + struct rcar_du_device *rcdu = to_rcar_vsp_plane(plane)->vsp->dev; + + if (property == rcdu->props.alpha) + *val = rstate->alpha; + else + return -EINVAL; + + return 0; +} + +static const struct drm_plane_funcs rcar_du_vsp_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .reset = rcar_du_vsp_plane_reset, + .set_property = drm_atomic_helper_plane_set_property, + .destroy = drm_plane_cleanup, + .atomic_duplicate_state = rcar_du_vsp_plane_atomic_duplicate_state, + .atomic_destroy_state = rcar_du_vsp_plane_atomic_destroy_state, + .atomic_set_property = rcar_du_vsp_plane_atomic_set_property, + .atomic_get_property = rcar_du_vsp_plane_atomic_get_property, +}; + +int rcar_du_vsp_init(struct rcar_du_vsp *vsp) +{ + struct rcar_du_device *rcdu = vsp->dev; + struct platform_device *pdev; + struct device_node *np; + unsigned int i; + int ret; + + /* Find the VSP device and initialize it. */ + np = of_parse_phandle(rcdu->dev->of_node, "vsps", vsp->index); + if (!np) { + dev_err(rcdu->dev, "vsps node not found\n"); + return -ENXIO; + } + + pdev = of_find_device_by_node(np); + of_node_put(np); + if (!pdev) + return -ENXIO; + + vsp->vsp = &pdev->dev; + + ret = vsp1_du_init(vsp->vsp); + if (ret < 0) + return ret; + + /* The VSP2D (Gen3) has 5 RPFs, but the VSP1D (Gen2) is limited to + * 4 RPFs. + */ + vsp->num_planes = rcdu->info->gen >= 3 ? 5 : 4; + + vsp->planes = devm_kcalloc(rcdu->dev, vsp->num_planes, + sizeof(*vsp->planes), GFP_KERNEL); + if (!vsp->planes) + return -ENOMEM; + + for (i = 0; i < vsp->num_planes; ++i) { + enum drm_plane_type type = i ? DRM_PLANE_TYPE_OVERLAY + : DRM_PLANE_TYPE_PRIMARY; + struct rcar_du_vsp_plane *plane = &vsp->planes[i]; + + plane->vsp = vsp; + plane->index = i; + + ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, + 1 << vsp->index, + &rcar_du_vsp_plane_funcs, + formats_kms, + ARRAY_SIZE(formats_kms), type, + NULL); + if (ret < 0) + return ret; + + drm_plane_helper_add(&plane->plane, + &rcar_du_vsp_plane_helper_funcs); + + if (type == DRM_PLANE_TYPE_PRIMARY) + continue; + + drm_object_attach_property(&plane->plane.base, + rcdu->props.alpha, 255); + } + + return 0; +} diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vsp.h b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h new file mode 100644 index 000000000000..df3bf3805c69 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_du_vsp.h @@ -0,0 +1,76 @@ +/* + * rcar_du_vsp.h -- R-Car Display Unit VSP-Based Compositor + * + * Copyright (C) 2015 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __RCAR_DU_VSP_H__ +#define __RCAR_DU_VSP_H__ + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> + +struct rcar_du_format_info; +struct rcar_du_vsp; + +struct rcar_du_vsp_plane { + struct drm_plane plane; + struct rcar_du_vsp *vsp; + unsigned int index; +}; + +struct rcar_du_vsp { + unsigned int index; + struct device *vsp; + struct rcar_du_device *dev; + struct rcar_du_vsp_plane *planes; + unsigned int num_planes; +}; + +static inline struct rcar_du_vsp_plane *to_rcar_vsp_plane(struct drm_plane *p) +{ + return container_of(p, struct rcar_du_vsp_plane, plane); +} + +/** + * struct rcar_du_vsp_plane_state - Driver-specific plane state + * @state: base DRM plane state + * @format: information about the pixel format used by the plane + * @alpha: value of the plane alpha property + */ +struct rcar_du_vsp_plane_state { + struct drm_plane_state state; + + const struct rcar_du_format_info *format; + + unsigned int alpha; +}; + +static inline struct rcar_du_vsp_plane_state * +to_rcar_vsp_plane_state(struct drm_plane_state *state) +{ + return container_of(state, struct rcar_du_vsp_plane_state, state); +} + +#ifdef CONFIG_DRM_RCAR_VSP +int rcar_du_vsp_init(struct rcar_du_vsp *vsp); +void rcar_du_vsp_enable(struct rcar_du_crtc *crtc); +void rcar_du_vsp_disable(struct rcar_du_crtc *crtc); +void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc); +void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc); +#else +static inline int rcar_du_vsp_init(struct rcar_du_vsp *vsp) { return 0; }; +static inline void rcar_du_vsp_enable(struct rcar_du_crtc *crtc) { }; +static inline void rcar_du_vsp_disable(struct rcar_du_crtc *crtc) { }; +static inline void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc) { }; +static inline void rcar_du_vsp_atomic_flush(struct rcar_du_crtc *crtc) { }; +#endif + +#endif /* __RCAR_DU_VSP_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h index 77cf9289ab65..d7d294ba2dbe 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h +++ b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h @@ -1,7 +1,7 @@ /* * rcar_lvds_regs.h -- R-Car LVDS Interface Registers Definitions * - * Copyright (C) 2013 Renesas Electronics Corporation + * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) * @@ -15,28 +15,38 @@ #define LVDCR0 0x0000 #define LVDCR0_DUSEL (1 << 15) -#define LVDCR0_DMD (1 << 12) +#define LVDCR0_DMD (1 << 12) /* Gen2 only */ #define LVDCR0_LVMD_MASK (0xf << 8) #define LVDCR0_LVMD_SHIFT 8 -#define LVDCR0_PLLEN (1 << 4) -#define LVDCR0_BEN (1 << 2) -#define LVDCR0_LVEN (1 << 1) +#define LVDCR0_PLLON (1 << 4) +#define LVDCR0_PWD (1 << 2) /* Gen3 only */ +#define LVDCR0_BEN (1 << 2) /* Gen2 only */ +#define LVDCR0_LVEN (1 << 1) /* Gen2 only */ #define LVDCR0_LVRES (1 << 0) #define LVDCR1 0x0004 -#define LVDCR1_CKSEL (1 << 15) -#define LVDCR1_CHSTBY(n) (3 << (2 + (n) * 2)) -#define LVDCR1_CLKSTBY (3 << 0) +#define LVDCR1_CKSEL (1 << 15) /* Gen2 only */ +#define LVDCR1_CHSTBY_GEN2(n) (3 << (2 + (n) * 2)) /* Gen2 only */ +#define LVDCR1_CHSTBY_GEN3(n) (1 << (2 + (n) * 2)) /* Gen3 only */ +#define LVDCR1_CLKSTBY_GEN2 (3 << 0) /* Gen2 only */ +#define LVDCR1_CLKSTBY_GEN3 (1 << 0) /* Gen3 only */ #define LVDPLLCR 0x0008 #define LVDPLLCR_CEEN (1 << 14) #define LVDPLLCR_FBEN (1 << 13) #define LVDPLLCR_COSEL (1 << 12) +/* Gen2 */ #define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0) #define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0) #define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0) #define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0) #define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 0) +/* Gen3 */ +#define LVDPLLCR_PLLDIVCNT_42M (0x014cb << 0) +#define LVDPLLCR_PLLDIVCNT_85M (0x00a45 << 0) +#define LVDPLLCR_PLLDIVCNT_128M (0x006c3 << 0) +#define LVDPLLCR_PLLDIVCNT_148M (0x046c1 << 0) +#define LVDPLLCR_PLLDIVCNT_MASK (0x7ffff << 0) #define LVDCTRCR 0x000c #define LVDCTRCR_CTR3SEL_ZERO (0 << 12) diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c index 77662068bc11..7975158064e8 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c @@ -878,7 +878,7 @@ static void dw_mipi_dsi_encoder_disable(struct drm_encoder *encoder) static void dw_mipi_dsi_encoder_commit(struct drm_encoder *encoder) { struct dw_mipi_dsi *dsi = encoder_to_dsi(encoder); - int mux = rockchip_drm_encoder_get_mux_id(dsi->dev->of_node, encoder); + int mux = drm_of_encoder_active_endpoint_id(dsi->dev->of_node, encoder); u32 interface_pix_fmt; u32 val; diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index c65ce8cb30d3..3d3cf2f8891e 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -204,7 +204,7 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) rockchip_drm_crtc_mode_config(encoder->crtc, DRM_MODE_CONNECTOR_HDMIA, ROCKCHIP_OUT_MODE_AAAA); - mux = rockchip_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder); + mux = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); if (mux) val = HDMI_SEL_VOP_LIT | (HDMI_SEL_VOP_LIT << 16); else diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index a0d51ccb6ea4..896da09e49ee 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -384,36 +384,6 @@ static const struct dev_pm_ops rockchip_drm_pm_ops = { rockchip_drm_sys_resume) }; -/* - * @node: device tree node containing encoder input ports - * @encoder: drm_encoder - */ -int rockchip_drm_encoder_get_mux_id(struct device_node *node, - struct drm_encoder *encoder) -{ - struct device_node *ep; - struct drm_crtc *crtc = encoder->crtc; - struct of_endpoint endpoint; - struct device_node *port; - int ret; - - if (!node || !crtc) - return -EINVAL; - - for_each_endpoint_of_node(node, ep) { - port = of_graph_get_remote_port(ep); - of_node_put(port); - if (port == crtc->port) { - ret = of_graph_parse_endpoint(ep, &endpoint); - of_node_put(ep); - return ret ?: endpoint.id; - } - } - - return -EINVAL; -} -EXPORT_SYMBOL_GPL(rockchip_drm_encoder_get_mux_id); - static int compare_of(struct device *dev, void *data) { struct device_node *np = data; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index bb8b076f1dbb..3529f692edb8 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -67,8 +67,6 @@ void rockchip_drm_atomic_work(struct work_struct *work); int rockchip_register_crtc_funcs(struct drm_crtc *crtc, const struct rockchip_crtc_funcs *crtc_funcs); void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc); -int rockchip_drm_encoder_get_mux_id(struct device_node *node, - struct drm_encoder *encoder); int rockchip_drm_crtc_mode_config(struct drm_crtc *crtc, int connector_type, int out_mode); int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 4802da8e6d6f..051e5e1b7ad6 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -21,25 +21,31 @@ #include "tilcdc_drv.h" #include "tilcdc_regs.h" +#define TILCDC_VBLANK_SAFETY_THRESHOLD_US 1000 + struct tilcdc_crtc { struct drm_crtc base; const struct tilcdc_panel_info *info; - uint32_t dirty; - dma_addr_t start, end; struct drm_pending_vblank_event *event; int dpms; wait_queue_head_t frame_done_wq; bool frame_done; + spinlock_t irq_lock; - /* fb currently set to scanout 0/1: */ - struct drm_framebuffer *scanout[2]; + ktime_t last_vblank; + + struct drm_framebuffer *curr_fb; + struct drm_framebuffer *next_fb; /* for deferred fb unref's: */ struct drm_flip_work unref_work; /* Only set if an external encoder is connected */ bool simulate_vesa_sync; + + int sync_lost_count; + bool frame_intact; }; #define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base) @@ -54,79 +60,53 @@ static void unref_worker(struct drm_flip_work *work, void *val) mutex_unlock(&dev->mode_config.mutex); } -static void set_scanout(struct drm_crtc *crtc, int n) +static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) { - static const uint32_t base_reg[] = { - LCDC_DMA_FB_BASE_ADDR_0_REG, - LCDC_DMA_FB_BASE_ADDR_1_REG, - }; - static const uint32_t ceil_reg[] = { - LCDC_DMA_FB_CEILING_ADDR_0_REG, - LCDC_DMA_FB_CEILING_ADDR_1_REG, - }; - static const uint32_t stat[] = { - LCDC_END_OF_FRAME0, LCDC_END_OF_FRAME1, - }; struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; - struct tilcdc_drm_private *priv = dev->dev_private; - - pm_runtime_get_sync(dev->dev); - tilcdc_write(dev, base_reg[n], tilcdc_crtc->start); - tilcdc_write(dev, ceil_reg[n], tilcdc_crtc->end); - if (tilcdc_crtc->scanout[n]) { - drm_flip_work_queue(&tilcdc_crtc->unref_work, tilcdc_crtc->scanout[n]); - drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); - } - tilcdc_crtc->scanout[n] = crtc->primary->fb; - drm_framebuffer_reference(tilcdc_crtc->scanout[n]); - tilcdc_crtc->dirty &= ~stat[n]; - pm_runtime_put_sync(dev->dev); -} - -static void update_scanout(struct drm_crtc *crtc) -{ - struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct drm_framebuffer *fb = crtc->primary->fb; struct drm_gem_cma_object *gem; unsigned int depth, bpp; + dma_addr_t start, end; drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); gem = drm_fb_cma_get_gem_obj(fb, 0); - tilcdc_crtc->start = gem->paddr + fb->offsets[0] + - (crtc->y * fb->pitches[0]) + (crtc->x * bpp/8); + start = gem->paddr + fb->offsets[0] + + crtc->y * fb->pitches[0] + + crtc->x * bpp / 8; - tilcdc_crtc->end = tilcdc_crtc->start + - (crtc->mode.vdisplay * fb->pitches[0]); + end = start + (crtc->mode.vdisplay * fb->pitches[0]); - if (tilcdc_crtc->dpms == DRM_MODE_DPMS_ON) { - /* already enabled, so just mark the frames that need - * updating and they will be updated on vblank: - */ - tilcdc_crtc->dirty |= LCDC_END_OF_FRAME0 | LCDC_END_OF_FRAME1; - drm_vblank_get(dev, 0); - } else { - /* not enabled yet, so update registers immediately: */ - set_scanout(crtc, 0); - set_scanout(crtc, 1); - } + tilcdc_write(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, start); + tilcdc_write(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG, end); + + if (tilcdc_crtc->curr_fb) + drm_flip_work_queue(&tilcdc_crtc->unref_work, + tilcdc_crtc->curr_fb); + + tilcdc_crtc->curr_fb = fb; } -static void start(struct drm_crtc *crtc) +static void reset(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct tilcdc_drm_private *priv = dev->dev_private; - if (priv->rev == 2) { - tilcdc_set(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); - msleep(1); - tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); - msleep(1); - } + if (priv->rev != 2) + return; + + tilcdc_set(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); + usleep_range(250, 1000); + tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); +} - tilcdc_set(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); +static void start(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + + reset(crtc); + + tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_PALETTE_LOAD_MODE(DATA_ONLY)); tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); } @@ -138,17 +118,31 @@ static void stop(struct drm_crtc *crtc) tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); } -static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode); static void tilcdc_crtc_destroy(struct drm_crtc *crtc) { struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + of_node_put(crtc->port); drm_crtc_cleanup(crtc); drm_flip_work_cleanup(&tilcdc_crtc->unref_work); +} + +static int tilcdc_verify_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb) +{ + struct drm_device *dev = crtc->dev; + unsigned int depth, bpp; + + drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); + + if (fb->pitches[0] != crtc->mode.hdisplay * bpp / 8) { + dev_err(dev->dev, + "Invalid pitch: fb and crtc widths must be the same"); + return -EINVAL; + } - kfree(tilcdc_crtc); + return 0; } static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, @@ -158,20 +152,48 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, { struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; + int r; + unsigned long flags; + s64 tdiff; + ktime_t next_vblank; + + r = tilcdc_verify_fb(crtc, fb); + if (r) + return r; if (tilcdc_crtc->event) { dev_err(dev->dev, "already pending page flip!\n"); return -EBUSY; } + drm_framebuffer_reference(fb); + crtc->primary->fb = fb; + + pm_runtime_get_sync(dev->dev); + + spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags); + + next_vblank = ktime_add_us(tilcdc_crtc->last_vblank, + 1000000 / crtc->hwmode.vrefresh); + + tdiff = ktime_to_us(ktime_sub(next_vblank, ktime_get())); + + if (tdiff >= TILCDC_VBLANK_SAFETY_THRESHOLD_US) + set_scanout(crtc, fb); + else + tilcdc_crtc->next_fb = fb; + tilcdc_crtc->event = event; - update_scanout(crtc); + + spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags); + + pm_runtime_put_sync(dev->dev); return 0; } -static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) +void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) { struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; @@ -186,10 +208,8 @@ static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) tilcdc_crtc->dpms = mode; - pm_runtime_get_sync(dev->dev); - if (mode == DRM_MODE_DPMS_ON) { - pm_runtime_forbid(dev->dev); + pm_runtime_get_sync(dev->dev); start(crtc); } else { tilcdc_crtc->frame_done = false; @@ -207,10 +227,23 @@ static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) if (ret == 0) dev_err(dev->dev, "timeout waiting for framedone\n"); } - pm_runtime_allow(dev->dev); - } - pm_runtime_put_sync(dev->dev); + pm_runtime_put_sync(dev->dev); + + if (tilcdc_crtc->next_fb) { + drm_flip_work_queue(&tilcdc_crtc->unref_work, + tilcdc_crtc->next_fb); + tilcdc_crtc->next_fb = NULL; + } + + if (tilcdc_crtc->curr_fb) { + drm_flip_work_queue(&tilcdc_crtc->unref_work, + tilcdc_crtc->curr_fb); + tilcdc_crtc->curr_fb = NULL; + } + + drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); + } } static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc, @@ -272,6 +305,10 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, if (WARN_ON(!info)) return -EINVAL; + ret = tilcdc_verify_fb(crtc, crtc->primary->fb); + if (ret) + return ret; + pm_runtime_get_sync(dev->dev); /* Configure the Burst Size and fifo threshold of DMA: */ @@ -419,8 +456,10 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, else tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER); + drm_framebuffer_reference(crtc->primary->fb); + + set_scanout(crtc, crtc->primary->fb); - update_scanout(crtc); tilcdc_crtc_update_clk(crtc); pm_runtime_put_sync(dev->dev); @@ -431,7 +470,21 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { - update_scanout(crtc); + struct drm_device *dev = crtc->dev; + int r; + + r = tilcdc_verify_fb(crtc, crtc->primary->fb); + if (r) + return r; + + drm_framebuffer_reference(crtc->primary->fb); + + pm_runtime_get_sync(dev->dev); + + set_scanout(crtc, crtc->primary->fb); + + pm_runtime_put_sync(dev->dev); + return 0; } @@ -573,7 +626,8 @@ void tilcdc_crtc_update_clk(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct tilcdc_drm_private *priv = dev->dev_private; int dpms = tilcdc_crtc->dpms; - unsigned int lcd_clk, div; + unsigned long lcd_clk; + const unsigned clkdiv = 2; /* using a fixed divider of 2 */ int ret; pm_runtime_get_sync(dev->dev); @@ -581,22 +635,21 @@ void tilcdc_crtc_update_clk(struct drm_crtc *crtc) if (dpms == DRM_MODE_DPMS_ON) tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); - /* in raster mode, minimum divisor is 2: */ - ret = clk_set_rate(priv->disp_clk, crtc->mode.clock * 1000 * 2); - if (ret) { + /* mode.clock is in KHz, set_rate wants parameter in Hz */ + ret = clk_set_rate(priv->clk, crtc->mode.clock * 1000 * clkdiv); + if (ret < 0) { dev_err(dev->dev, "failed to set display clock rate to: %d\n", crtc->mode.clock); goto out; } lcd_clk = clk_get_rate(priv->clk); - div = lcd_clk / (crtc->mode.clock * 1000); - DBG("lcd_clk=%u, mode clock=%d, div=%u", lcd_clk, crtc->mode.clock, div); - DBG("fck=%lu, dpll_disp_ck=%lu", clk_get_rate(priv->clk), clk_get_rate(priv->disp_clk)); + DBG("lcd_clk=%lu, mode clock=%d, div=%u", + lcd_clk, crtc->mode.clock, clkdiv); /* Configure the LCD clock divisor. */ - tilcdc_write(dev, LCDC_CTRL_REG, LCDC_CLK_DIVISOR(div) | + tilcdc_write(dev, LCDC_CTRL_REG, LCDC_CLK_DIVISOR(clkdiv) | LCDC_RASTER_MODE); if (priv->rev == 2) @@ -611,44 +664,58 @@ out: pm_runtime_put_sync(dev->dev); } +#define SYNC_LOST_COUNT_LIMIT 50 + irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) { struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; struct tilcdc_drm_private *priv = dev->dev_private; - uint32_t stat = tilcdc_read_irqstatus(dev); + uint32_t stat; - if ((stat & LCDC_SYNC_LOST) && (stat & LCDC_FIFO_UNDERFLOW)) { - stop(crtc); - dev_err(dev->dev, "error: %08x\n", stat); - tilcdc_clear_irqstatus(dev, stat); - start(crtc); - } else if (stat & LCDC_PL_LOAD_DONE) { - tilcdc_clear_irqstatus(dev, stat); - } else { - struct drm_pending_vblank_event *event; + stat = tilcdc_read_irqstatus(dev); + tilcdc_clear_irqstatus(dev, stat); + + if (stat & LCDC_END_OF_FRAME0) { unsigned long flags; - uint32_t dirty = tilcdc_crtc->dirty & stat; + bool skip_event = false; + ktime_t now; + + now = ktime_get(); - tilcdc_clear_irqstatus(dev, stat); + drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); - if (dirty & LCDC_END_OF_FRAME0) - set_scanout(crtc, 0); + spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags); - if (dirty & LCDC_END_OF_FRAME1) - set_scanout(crtc, 1); + tilcdc_crtc->last_vblank = now; + + if (tilcdc_crtc->next_fb) { + set_scanout(crtc, tilcdc_crtc->next_fb); + tilcdc_crtc->next_fb = NULL; + skip_event = true; + } + + spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags); drm_handle_vblank(dev, 0); - spin_lock_irqsave(&dev->event_lock, flags); - event = tilcdc_crtc->event; - tilcdc_crtc->event = NULL; - if (event) - drm_send_vblank_event(dev, 0, event); - spin_unlock_irqrestore(&dev->event_lock, flags); + if (!skip_event) { + struct drm_pending_vblank_event *event; + + spin_lock_irqsave(&dev->event_lock, flags); + + event = tilcdc_crtc->event; + tilcdc_crtc->event = NULL; + if (event) + drm_send_vblank_event(dev, 0, event); + + spin_unlock_irqrestore(&dev->event_lock, flags); + } - if (dirty && !tilcdc_crtc->dirty) - drm_vblank_put(dev, 0); + if (tilcdc_crtc->frame_intact) + tilcdc_crtc->sync_lost_count = 0; + else + tilcdc_crtc->frame_intact = true; } if (priv->rev == 2) { @@ -659,16 +726,34 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0); } + if (stat & LCDC_SYNC_LOST) { + dev_err_ratelimited(dev->dev, "%s(0x%08x): Sync lost", + __func__, stat); + tilcdc_crtc->frame_intact = false; + if (tilcdc_crtc->sync_lost_count++ > SYNC_LOST_COUNT_LIMIT) { + dev_err(dev->dev, + "%s(0x%08x): Sync lost flood detected, disabling the interrupt", + __func__, stat); + tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, + LCDC_SYNC_LOST); + } + } + + if (stat & LCDC_FIFO_UNDERFLOW) + dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underfow", + __func__, stat); + return IRQ_HANDLED; } struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) { + struct tilcdc_drm_private *priv = dev->dev_private; struct tilcdc_crtc *tilcdc_crtc; struct drm_crtc *crtc; int ret; - tilcdc_crtc = kzalloc(sizeof(*tilcdc_crtc), GFP_KERNEL); + tilcdc_crtc = devm_kzalloc(dev->dev, sizeof(*tilcdc_crtc), GFP_KERNEL); if (!tilcdc_crtc) { dev_err(dev->dev, "allocation failed\n"); return NULL; @@ -682,12 +767,32 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) drm_flip_work_init(&tilcdc_crtc->unref_work, "unref", unref_worker); + spin_lock_init(&tilcdc_crtc->irq_lock); + ret = drm_crtc_init(dev, crtc, &tilcdc_crtc_funcs); if (ret < 0) goto fail; drm_crtc_helper_add(crtc, &tilcdc_crtc_helper_funcs); + if (priv->is_componentized) { + struct device_node *ports = + of_get_child_by_name(dev->dev->of_node, "ports"); + + if (ports) { + crtc->port = of_get_child_by_name(ports, "port"); + of_node_put(ports); + } else { + crtc->port = + of_get_child_by_name(dev->dev->of_node, "port"); + } + if (!crtc->port) { /* This should never happen */ + dev_err(dev->dev, "Port node not found in %s\n", + dev->dev->of_node->full_name); + goto fail; + } + } + return crtc; fail: diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 8190ac3b1b32..709bc903524d 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -18,6 +18,8 @@ /* LCDC DRM driver, based on da8xx-fb */ #include <linux/component.h> +#include <linux/pinctrl/consumer.h> +#include <linux/suspend.h> #include "tilcdc_drv.h" #include "tilcdc_regs.h" @@ -110,6 +112,8 @@ static int tilcdc_unload(struct drm_device *dev) { struct tilcdc_drm_private *priv = dev->dev_private; + tilcdc_crtc_dpms(priv->crtc, DRM_MODE_DPMS_OFF); + tilcdc_remove_external_encoders(dev); drm_fbdev_cma_fini(priv->fbdev); @@ -139,11 +143,11 @@ static int tilcdc_unload(struct drm_device *dev) pm_runtime_disable(dev->dev); - kfree(priv); - return 0; } +static size_t tilcdc_num_regs(void); + static int tilcdc_load(struct drm_device *dev, unsigned long flags) { struct platform_device *pdev = dev->platformdev; @@ -154,8 +158,12 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) u32 bpp = 0; int ret; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { + priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL); + if (priv) + priv->saved_register = + devm_kcalloc(dev->dev, tilcdc_num_regs(), + sizeof(*priv->saved_register), GFP_KERNEL); + if (!priv || !priv->saved_register) { dev_err(dev->dev, "failed to allocate private data\n"); return -ENOMEM; } @@ -168,7 +176,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) priv->wq = alloc_ordered_workqueue("tilcdc", 0); if (!priv->wq) { ret = -ENOMEM; - goto fail_free_priv; + goto fail_unset_priv; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -192,13 +200,6 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) goto fail_iounmap; } - priv->disp_clk = clk_get(dev->dev, "dpll_disp_ck"); - if (IS_ERR(priv->clk)) { - dev_err(dev->dev, "failed to get display clock\n"); - ret = -ENODEV; - goto fail_put_clk; - } - #ifdef CONFIG_CPU_FREQ priv->lcd_fck_rate = clk_get_rate(priv->clk); priv->freq_transition.notifier_call = cpufreq_transition; @@ -206,7 +207,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) CPUFREQ_TRANSITION_NOTIFIER); if (ret) { dev_err(dev->dev, "failed to register cpufreq notifier\n"); - goto fail_put_disp_clk; + goto fail_put_clk; } #endif @@ -227,7 +228,6 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) DBG("Maximum Pixel Clock Value %dKHz", priv->max_pixelclock); pm_runtime_enable(dev->dev); - pm_runtime_irq_safe(dev->dev); /* Determine LCD IP Version */ pm_runtime_get_sync(dev->dev); @@ -330,11 +330,9 @@ fail_cpufreq_unregister: #ifdef CONFIG_CPU_FREQ cpufreq_unregister_notifier(&priv->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); -fail_put_disp_clk: - clk_put(priv->disp_clk); -#endif fail_put_clk: +#endif clk_put(priv->clk); fail_iounmap: @@ -344,9 +342,9 @@ fail_free_wq: flush_workqueue(priv->wq); destroy_workqueue(priv->wq); -fail_free_priv: +fail_unset_priv: dev->dev_private = NULL; - kfree(priv); + return ret; } @@ -373,10 +371,14 @@ static int tilcdc_irq_postinstall(struct drm_device *dev) struct tilcdc_drm_private *priv = dev->dev_private; /* enable FIFO underflow irq: */ - if (priv->rev == 1) + if (priv->rev == 1) { tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_UNDERFLOW_INT_ENA); - else - tilcdc_set(dev, LCDC_INT_ENABLE_SET_REG, LCDC_V2_UNDERFLOW_INT_ENA); + } else { + tilcdc_write(dev, LCDC_INT_ENABLE_SET_REG, + LCDC_V2_UNDERFLOW_INT_ENA | + LCDC_V2_END_OF_FRAME0_INT_ENA | + LCDC_FRAME_DONE | LCDC_SYNC_LOST); + } return 0; } @@ -391,43 +393,21 @@ static void tilcdc_irq_uninstall(struct drm_device *dev) LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA); tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_V1_END_OF_FRAME_INT_ENA); } else { - tilcdc_clear(dev, LCDC_INT_ENABLE_SET_REG, + tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA | - LCDC_V2_END_OF_FRAME0_INT_ENA | LCDC_V2_END_OF_FRAME1_INT_ENA | - LCDC_FRAME_DONE); - } - -} - -static void enable_vblank(struct drm_device *dev, bool enable) -{ - struct tilcdc_drm_private *priv = dev->dev_private; - u32 reg, mask; - - if (priv->rev == 1) { - reg = LCDC_DMA_CTRL_REG; - mask = LCDC_V1_END_OF_FRAME_INT_ENA; - } else { - reg = LCDC_INT_ENABLE_SET_REG; - mask = LCDC_V2_END_OF_FRAME0_INT_ENA | - LCDC_V2_END_OF_FRAME1_INT_ENA | LCDC_FRAME_DONE; + LCDC_V2_END_OF_FRAME0_INT_ENA | + LCDC_FRAME_DONE | LCDC_SYNC_LOST); } - - if (enable) - tilcdc_set(dev, reg, mask); - else - tilcdc_clear(dev, reg, mask); } static int tilcdc_enable_vblank(struct drm_device *dev, unsigned int pipe) { - enable_vblank(dev, true); return 0; } static void tilcdc_disable_vblank(struct drm_device *dev, unsigned int pipe) { - enable_vblank(dev, false); + return; } #if defined(CONFIG_DEBUG_FS) || defined(CONFIG_PM_SLEEP) @@ -454,13 +434,22 @@ static const struct { /* new in revision 2: */ REG(2, false, LCDC_RAW_STAT_REG), REG(2, false, LCDC_MASKED_STAT_REG), - REG(2, false, LCDC_INT_ENABLE_SET_REG), + REG(2, true, LCDC_INT_ENABLE_SET_REG), REG(2, false, LCDC_INT_ENABLE_CLR_REG), REG(2, false, LCDC_END_OF_INT_IND_REG), REG(2, true, LCDC_CLK_ENABLE_REG), - REG(2, true, LCDC_INT_ENABLE_SET_REG), #undef REG }; + +static size_t tilcdc_num_regs(void) +{ + return ARRAY_SIZE(registers); +} +#else +static size_t tilcdc_num_regs(void) +{ + return 0; +} #endif #ifdef CONFIG_DEBUG_FS @@ -547,7 +536,8 @@ static const struct file_operations fops = { }; static struct drm_driver tilcdc_driver = { - .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, + .driver_features = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | + DRIVER_PRIME), .load = tilcdc_load, .unload = tilcdc_unload, .lastclose = tilcdc_lastclose, @@ -564,6 +554,16 @@ static struct drm_driver tilcdc_driver = { .dumb_create = drm_gem_cma_dumb_create, .dumb_map_offset = drm_gem_cma_dumb_map_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_import = drm_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, #ifdef CONFIG_DEBUG_FS .debugfs_init = tilcdc_debugfs_init, .debugfs_cleanup = tilcdc_debugfs_cleanup, @@ -589,11 +589,24 @@ static int tilcdc_pm_suspend(struct device *dev) drm_kms_helper_poll_disable(ddev); + /* Select sleep pin state */ + pinctrl_pm_select_sleep_state(dev); + + if (pm_runtime_suspended(dev)) { + priv->ctx_valid = false; + return 0; + } + + /* Disable the LCDC controller, to avoid locking up the PRCM */ + tilcdc_crtc_dpms(priv->crtc, DRM_MODE_DPMS_OFF); + /* Save register state: */ for (i = 0; i < ARRAY_SIZE(registers); i++) if (registers[i].save && (priv->rev >= registers[i].rev)) priv->saved_register[n++] = tilcdc_read(ddev, registers[i].reg); + priv->ctx_valid = true; + return 0; } @@ -603,10 +616,17 @@ static int tilcdc_pm_resume(struct device *dev) struct tilcdc_drm_private *priv = ddev->dev_private; unsigned i, n = 0; - /* Restore register state: */ - for (i = 0; i < ARRAY_SIZE(registers); i++) - if (registers[i].save && (priv->rev >= registers[i].rev)) - tilcdc_write(ddev, registers[i].reg, priv->saved_register[n++]); + /* Select default pin state */ + pinctrl_pm_select_default_state(dev); + + if (priv->ctx_valid == true) { + /* Restore register state: */ + for (i = 0; i < ARRAY_SIZE(registers); i++) + if (registers[i].save && + (priv->rev >= registers[i].rev)) + tilcdc_write(ddev, registers[i].reg, + priv->saved_register[n++]); + } drm_kms_helper_poll_enable(ddev); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h index 66105d8dc620..c1de18bae415 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h @@ -49,7 +49,6 @@ struct tilcdc_drm_private { void __iomem *mmio; - struct clk *disp_clk; /* display dpll */ struct clk *clk; /* functional clock */ int rev; /* IP revision */ @@ -67,7 +66,8 @@ struct tilcdc_drm_private { uint32_t max_width; /* register contents saved across suspend/resume: */ - u32 saved_register[12]; + u32 *saved_register; + bool ctx_valid; #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; @@ -171,5 +171,6 @@ void tilcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *crtc, bool simulate_vesa_sync); int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode); int tilcdc_crtc_max_width(struct drm_crtc *crtc); +void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode); #endif /* __TILCDC_DRV_H__ */ diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index 8dcf02a79b23..ff7774c17d7c 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -45,14 +45,6 @@ struct panel_encoder { }; #define to_panel_encoder(x) container_of(x, struct panel_encoder, base) - -static void panel_encoder_destroy(struct drm_encoder *encoder) -{ - struct panel_encoder *panel_encoder = to_panel_encoder(encoder); - drm_encoder_cleanup(encoder); - kfree(panel_encoder); -} - static void panel_encoder_dpms(struct drm_encoder *encoder, int mode) { struct panel_encoder *panel_encoder = to_panel_encoder(encoder); @@ -90,7 +82,7 @@ static void panel_encoder_mode_set(struct drm_encoder *encoder, } static const struct drm_encoder_funcs panel_encoder_funcs = { - .destroy = panel_encoder_destroy, + .destroy = drm_encoder_cleanup, }; static const struct drm_encoder_helper_funcs panel_encoder_helper_funcs = { @@ -107,7 +99,8 @@ static struct drm_encoder *panel_encoder_create(struct drm_device *dev, struct drm_encoder *encoder; int ret; - panel_encoder = kzalloc(sizeof(*panel_encoder), GFP_KERNEL); + panel_encoder = devm_kzalloc(dev->dev, sizeof(*panel_encoder), + GFP_KERNEL); if (!panel_encoder) { dev_err(dev->dev, "allocation failed\n"); return NULL; @@ -128,7 +121,7 @@ static struct drm_encoder *panel_encoder_create(struct drm_device *dev, return encoder; fail: - panel_encoder_destroy(encoder); + drm_encoder_cleanup(encoder); return NULL; } @@ -147,10 +140,8 @@ struct panel_connector { static void panel_connector_destroy(struct drm_connector *connector) { - struct panel_connector *panel_connector = to_panel_connector(connector); drm_connector_unregister(connector); drm_connector_cleanup(connector); - kfree(panel_connector); } static enum drm_connector_status panel_connector_detect( @@ -223,7 +214,8 @@ static struct drm_connector *panel_connector_create(struct drm_device *dev, struct drm_connector *connector; int ret; - panel_connector = kzalloc(sizeof(*panel_connector), GFP_KERNEL); + panel_connector = devm_kzalloc(dev->dev, sizeof(*panel_connector), + GFP_KERNEL); if (!panel_connector) { dev_err(dev->dev, "allocation failed\n"); return NULL; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c index 1c230172b402..7716f42f8aab 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c @@ -54,14 +54,6 @@ struct tfp410_encoder { }; #define to_tfp410_encoder(x) container_of(x, struct tfp410_encoder, base) - -static void tfp410_encoder_destroy(struct drm_encoder *encoder) -{ - struct tfp410_encoder *tfp410_encoder = to_tfp410_encoder(encoder); - drm_encoder_cleanup(encoder); - kfree(tfp410_encoder); -} - static void tfp410_encoder_dpms(struct drm_encoder *encoder, int mode) { struct tfp410_encoder *tfp410_encoder = to_tfp410_encoder(encoder); @@ -99,7 +91,7 @@ static void tfp410_encoder_mode_set(struct drm_encoder *encoder, } static const struct drm_encoder_funcs tfp410_encoder_funcs = { - .destroy = tfp410_encoder_destroy, + .destroy = drm_encoder_cleanup, }; static const struct drm_encoder_helper_funcs tfp410_encoder_helper_funcs = { @@ -116,7 +108,8 @@ static struct drm_encoder *tfp410_encoder_create(struct drm_device *dev, struct drm_encoder *encoder; int ret; - tfp410_encoder = kzalloc(sizeof(*tfp410_encoder), GFP_KERNEL); + tfp410_encoder = devm_kzalloc(dev->dev, sizeof(*tfp410_encoder), + GFP_KERNEL); if (!tfp410_encoder) { dev_err(dev->dev, "allocation failed\n"); return NULL; @@ -138,7 +131,7 @@ static struct drm_encoder *tfp410_encoder_create(struct drm_device *dev, return encoder; fail: - tfp410_encoder_destroy(encoder); + drm_encoder_cleanup(encoder); return NULL; } @@ -157,10 +150,8 @@ struct tfp410_connector { static void tfp410_connector_destroy(struct drm_connector *connector) { - struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector); drm_connector_unregister(connector); drm_connector_cleanup(connector); - kfree(tfp410_connector); } static enum drm_connector_status tfp410_connector_detect( @@ -228,7 +219,8 @@ static struct drm_connector *tfp410_connector_create(struct drm_device *dev, struct drm_connector *connector; int ret; - tfp410_connector = kzalloc(sizeof(*tfp410_connector), GFP_KERNEL); + tfp410_connector = devm_kzalloc(dev->dev, sizeof(*tfp410_connector), + GFP_KERNEL); if (!tfp410_connector) { dev_err(dev->dev, "allocation failed\n"); return NULL; @@ -313,7 +305,7 @@ static int tfp410_probe(struct platform_device *pdev) return -ENXIO; } - tfp410_mod = kzalloc(sizeof(*tfp410_mod), GFP_KERNEL); + tfp410_mod = devm_kzalloc(&pdev->dev, sizeof(*tfp410_mod), GFP_KERNEL); if (!tfp410_mod) return -ENOMEM; @@ -366,7 +358,6 @@ fail_adapter: i2c_put_adapter(tfp410_mod->i2c); fail: - kfree(tfp410_mod); tilcdc_module_cleanup(mod); return ret; } @@ -380,7 +371,6 @@ static int tfp410_remove(struct platform_device *pdev) gpio_free(tfp410_mod->gpio); tilcdc_module_cleanup(mod); - kfree(tfp410_mod); return 0; } diff --git a/drivers/gpu/ipu-v3/ipu-dc.c b/drivers/gpu/ipu-v3/ipu-dc.c index d3ad5347342c..2f29780e7c68 100644 --- a/drivers/gpu/ipu-v3/ipu-dc.c +++ b/drivers/gpu/ipu-v3/ipu-dc.c @@ -171,6 +171,7 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, u32 bus_format, u32 width) { struct ipu_dc_priv *priv = dc->priv; + int addr, sync; u32 reg = 0; int map; @@ -182,41 +183,39 @@ int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced, return map; } - if (interlaced) { - int addr; - - if (dc->di) - addr = 1; - else - addr = 0; + /* + * In interlaced mode we need more counters to create the asymmetric + * per-field VSYNC signals. The pixel active signal synchronising DC + * to DI moves to signal generator #6 (see ipu-di.c). In progressive + * mode counter #5 is used. + */ + sync = interlaced ? 6 : 5; + + /* Reserve 5 microcode template words for each DI */ + if (dc->di) + addr = 5; + else + addr = 0; + if (interlaced) { dc_link_event(dc, DC_EVT_NL, addr, 3); dc_link_event(dc, DC_EVT_EOL, addr, 2); dc_link_event(dc, DC_EVT_NEW_DATA, addr, 1); /* Init template microcode */ - dc_write_tmpl(dc, addr, WROD(0), 0, map, SYNC_WAVE, 0, 6, 1); + dc_write_tmpl(dc, addr, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1); } else { - if (dc->di) { - dc_link_event(dc, DC_EVT_NL, 2, 3); - dc_link_event(dc, DC_EVT_EOL, 3, 2); - dc_link_event(dc, DC_EVT_NEW_DATA, 1, 1); - /* Init template microcode */ - dc_write_tmpl(dc, 2, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1); - dc_write_tmpl(dc, 3, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0); - dc_write_tmpl(dc, 4, WRG, 0, map, NULL_WAVE, 0, 0, 1); - dc_write_tmpl(dc, 1, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1); - } else { - dc_link_event(dc, DC_EVT_NL, 5, 3); - dc_link_event(dc, DC_EVT_EOL, 6, 2); - dc_link_event(dc, DC_EVT_NEW_DATA, 8, 1); - /* Init template microcode */ - dc_write_tmpl(dc, 5, WROD(0), 0, map, SYNC_WAVE, 8, 5, 1); - dc_write_tmpl(dc, 6, WROD(0), 0, map, SYNC_WAVE, 4, 5, 0); - dc_write_tmpl(dc, 7, WRG, 0, map, NULL_WAVE, 0, 0, 1); - dc_write_tmpl(dc, 8, WROD(0), 0, map, SYNC_WAVE, 0, 5, 1); - } + dc_link_event(dc, DC_EVT_NL, addr + 2, 3); + dc_link_event(dc, DC_EVT_EOL, addr + 3, 2); + dc_link_event(dc, DC_EVT_NEW_DATA, addr + 1, 1); + + /* Init template microcode */ + dc_write_tmpl(dc, addr + 2, WROD(0), 0, map, SYNC_WAVE, 8, sync, 1); + dc_write_tmpl(dc, addr + 3, WROD(0), 0, map, SYNC_WAVE, 4, sync, 0); + dc_write_tmpl(dc, addr + 4, WRG, 0, map, NULL_WAVE, 0, 0, 1); + dc_write_tmpl(dc, addr + 1, WROD(0), 0, map, SYNC_WAVE, 0, sync, 1); } + dc_link_event(dc, DC_EVT_NF, 0, 0); dc_link_event(dc, DC_EVT_NFIELD, 0, 0); dc_link_event(dc, DC_EVT_EOF, 0, 0); |