| From 4f626a4ac8f57ddabf06d03870adab91e463217f Mon Sep 17 00:00:00 2001 |
| From: Roman Kapl <rka@sysgo.com> |
| Date: Mon, 30 Oct 2017 11:56:13 +0100 |
| Subject: drm/radeon: fix atombios on big endian |
| |
| From: Roman Kapl <rka@sysgo.com> |
| |
| commit 4f626a4ac8f57ddabf06d03870adab91e463217f upstream. |
| |
| The function for byteswapping the data send to/from atombios was buggy for |
| num_bytes not divisible by four. The function must be aware of the fact |
| that after byte-swapping the u32 units, valid bytes might end up after the |
| num_bytes boundary. |
| |
| This patch was tested on kernel 3.12 and allowed us to sucesfully use |
| DisplayPort on and Radeon SI card. Namely it fixed the link training and |
| EDID readout. |
| |
| The function is patched both in radeon and amd drivers, since the functions |
| and the fixes are identical. |
| |
| Signed-off-by: Roman Kapl <rka@sysgo.com> |
| Signed-off-by: Alex Deucher <alexander.deucher@amd.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c | 38 ++++++++++++--------------- |
| drivers/gpu/drm/radeon/atombios_dp.c | 38 ++++++++++++--------------- |
| 2 files changed, 36 insertions(+), 40 deletions(-) |
| |
| --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c |
| +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c |
| @@ -1575,34 +1575,32 @@ void amdgpu_atombios_scratch_regs_restor |
| WREG32(mmBIOS_SCRATCH_0 + i, adev->bios_scratch[i]); |
| } |
| |
| -/* Atom needs data in little endian format |
| - * so swap as appropriate when copying data to |
| - * or from atom. Note that atom operates on |
| - * dw units. |
| +/* Atom needs data in little endian format so swap as appropriate when copying |
| + * data to or from atom. Note that atom operates on dw units. |
| + * |
| + * Use to_le=true when sending data to atom and provide at least |
| + * ALIGN(num_bytes,4) bytes in the dst buffer. |
| + * |
| + * Use to_le=false when receiving data from atom and provide ALIGN(num_bytes,4) |
| + * byes in the src buffer. |
| */ |
| void amdgpu_atombios_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le) |
| { |
| #ifdef __BIG_ENDIAN |
| - u8 src_tmp[20], dst_tmp[20]; /* used for byteswapping */ |
| - u32 *dst32, *src32; |
| + u32 src_tmp[5], dst_tmp[5]; |
| int i; |
| + u8 align_num_bytes = ALIGN(num_bytes, 4); |
| |
| - memcpy(src_tmp, src, num_bytes); |
| - src32 = (u32 *)src_tmp; |
| - dst32 = (u32 *)dst_tmp; |
| if (to_le) { |
| - for (i = 0; i < ((num_bytes + 3) / 4); i++) |
| - dst32[i] = cpu_to_le32(src32[i]); |
| - memcpy(dst, dst_tmp, num_bytes); |
| + memcpy(src_tmp, src, num_bytes); |
| + for (i = 0; i < align_num_bytes / 4; i++) |
| + dst_tmp[i] = cpu_to_le32(src_tmp[i]); |
| + memcpy(dst, dst_tmp, align_num_bytes); |
| } else { |
| - u8 dws = num_bytes & ~3; |
| - for (i = 0; i < ((num_bytes + 3) / 4); i++) |
| - dst32[i] = le32_to_cpu(src32[i]); |
| - memcpy(dst, dst_tmp, dws); |
| - if (num_bytes % 4) { |
| - for (i = 0; i < (num_bytes % 4); i++) |
| - dst[dws+i] = dst_tmp[dws+i]; |
| - } |
| + memcpy(src_tmp, src, align_num_bytes); |
| + for (i = 0; i < align_num_bytes / 4; i++) |
| + dst_tmp[i] = le32_to_cpu(src_tmp[i]); |
| + memcpy(dst, dst_tmp, num_bytes); |
| } |
| #else |
| memcpy(dst, src, num_bytes); |
| --- a/drivers/gpu/drm/radeon/atombios_dp.c |
| +++ b/drivers/gpu/drm/radeon/atombios_dp.c |
| @@ -45,34 +45,32 @@ static char *pre_emph_names[] = { |
| |
| /***** radeon AUX functions *****/ |
| |
| -/* Atom needs data in little endian format |
| - * so swap as appropriate when copying data to |
| - * or from atom. Note that atom operates on |
| - * dw units. |
| +/* Atom needs data in little endian format so swap as appropriate when copying |
| + * data to or from atom. Note that atom operates on dw units. |
| + * |
| + * Use to_le=true when sending data to atom and provide at least |
| + * ALIGN(num_bytes,4) bytes in the dst buffer. |
| + * |
| + * Use to_le=false when receiving data from atom and provide ALIGN(num_bytes,4) |
| + * byes in the src buffer. |
| */ |
| void radeon_atom_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le) |
| { |
| #ifdef __BIG_ENDIAN |
| - u8 src_tmp[20], dst_tmp[20]; /* used for byteswapping */ |
| - u32 *dst32, *src32; |
| + u32 src_tmp[5], dst_tmp[5]; |
| int i; |
| + u8 align_num_bytes = ALIGN(num_bytes, 4); |
| |
| - memcpy(src_tmp, src, num_bytes); |
| - src32 = (u32 *)src_tmp; |
| - dst32 = (u32 *)dst_tmp; |
| if (to_le) { |
| - for (i = 0; i < ((num_bytes + 3) / 4); i++) |
| - dst32[i] = cpu_to_le32(src32[i]); |
| - memcpy(dst, dst_tmp, num_bytes); |
| + memcpy(src_tmp, src, num_bytes); |
| + for (i = 0; i < align_num_bytes / 4; i++) |
| + dst_tmp[i] = cpu_to_le32(src_tmp[i]); |
| + memcpy(dst, dst_tmp, align_num_bytes); |
| } else { |
| - u8 dws = num_bytes & ~3; |
| - for (i = 0; i < ((num_bytes + 3) / 4); i++) |
| - dst32[i] = le32_to_cpu(src32[i]); |
| - memcpy(dst, dst_tmp, dws); |
| - if (num_bytes % 4) { |
| - for (i = 0; i < (num_bytes % 4); i++) |
| - dst[dws+i] = dst_tmp[dws+i]; |
| - } |
| + memcpy(src_tmp, src, align_num_bytes); |
| + for (i = 0; i < align_num_bytes / 4; i++) |
| + dst_tmp[i] = le32_to_cpu(src_tmp[i]); |
| + memcpy(dst, dst_tmp, num_bytes); |
| } |
| #else |
| memcpy(dst, src, num_bytes); |