/*
* Copyright 2015 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#include <linux/string.h>
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <drm/drm_probe_helper.h>
#include <drm/amdgpu_drm.h>
#include <drm/drm_edid.h>
#include "dm_services.h"
#include "amdgpu.h"
#include "dc.h"
#include "amdgpu_dm.h"
#include "amdgpu_dm_irq.h"
#include "amdgpu_dm_mst_types.h"
#include "dm_helpers.h"
#include "ddc_service_types.h"
struct monitor_patch_info {
unsigned int manufacturer_id;
unsigned int product_id;
void (*patch_func)(struct dc_edid_caps *edid_caps, unsigned int param);
unsigned int patch_param;
};
static void set_max_dsc_bpp_limit(struct dc_edid_caps *edid_caps, unsigned int param);
static const struct monitor_patch_info monitor_patch_table[] = {
{0x6D1E, 0x5BBF, set_max_dsc_bpp_limit, 15},
{0x6D1E, 0x5B9A, set_max_dsc_bpp_limit, 15},
};
static void set_max_dsc_bpp_limit(struct dc_edid_caps *edid_caps, unsigned int param)
{
if (edid_caps)
edid_caps->panel_patch.max_dsc_target_bpp_limit = param;
}
static int amdgpu_dm_patch_edid_caps(struct dc_edid_caps *edid_caps)
{
int i, ret = 0;
for (i = 0; i < ARRAY_SIZE(monitor_patch_table); i++)
if ((edid_caps->manufacturer_id == monitor_patch_table[i].manufacturer_id)
&& (edid_caps->product_id == monitor_patch_table[i].product_id)) {
monitor_patch_table[i].patch_func(edid_caps, monitor_patch_table[i].patch_param);
ret++;
}
return ret;
}
/* dm_helpers_parse_edid_caps
*
* Parse edid caps
*
* @edid: [in] pointer to edid
* edid_caps: [in] pointer to edid caps
* @return
* void
* */
enum dc_edid_status dm_helpers_parse_edid_caps(
struct dc_link *link,
const struct dc_edid *edid,
struct dc_edid_caps *edid_caps)
{
struct amdgpu_dm_connector *aconnector = link->priv;
struct drm_connector *connector = &aconnector->base;
struct edid *edid_buf = edid ? (struct edid *) edid->raw_edid : NULL;
struct cea_sad *sads;
int sad_count = -1;
int sadb_count = -1;
int i = 0;
uint8_t *sadb = NULL;
enum dc_edid_status result = EDID_OK;
if (!edid_caps || !edid)
return EDID_BAD_INPUT;
if (!drm_edid_is_valid(edid_buf))
result = EDID_BAD_CHECKSUM;
edid_caps->manufacturer_id = (uint16_t) edid_buf->mfg_id[0] |
((uint16_t) edid_buf->mfg_id[1])<<8;
edid_caps->product_id = (uint16_t) edid_buf->prod_code[0] |
((uint16_t) edid_buf->prod_code[1])<<8;
edid_caps->serial_number = edid_buf->serial;
edid_caps->manufacture_week = edid_buf->mfg_week;
edid_caps->manufacture_year = edid_buf->mfg_year;
drm_edid_get_monitor_name(edid_buf,
edid_caps->display_name,
AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS);
edid_caps->edid_hdmi = connector->display_info.is_hdmi;
sad_count = drm_edid_to_sad((struct edid *) edid->raw_edid, &sads);
if (sad_count <= 0)
return result;
edid_caps->audio_mode_count = sad_count < DC_MAX_AUDIO_DESC_COUNT ? sad_count : DC_MAX_AUDIO_DESC_COUNT;
for (i = 0; i < edid_caps->audio_mode_count; ++i) {
struct cea_sad *sad = &sads[i];
edid_caps->audio_modes[i].format_code = sad->format;
edid_caps->audio_modes[i].channel_count = sad->channels + 1;
edid_caps->audio_modes[i].sample_rate = sad->freq;
edid_caps->audio_modes[i].sample_size = sad->byte2;
}
sadb_count = drm_edid_to_speaker_allocation((struct edid *) edid->raw_edid, &sadb);
if (sadb_count < 0) {
DRM_ERROR("Couldn't read Speaker Allocation Data Block: %d\n", sadb_count);
sadb_count = 0;
}
if (sadb_count)
edid_caps->speaker_flags = sadb[0];
else
edid_caps->speaker_flags = DEFAULT_SPEAKER_LOCATION;
kfree(sads);
kfree(sadb);
amdgpu_dm_patch_edid_caps(edid_caps);
return result;
}
static void get_payload_table(
struct amdgpu_dm_connector *aconnector,
struct dp_mst_stream_allocation_table *proposed_table)
{
int i;
struct drm_dp_mst_topology_mgr *mst_mgr