Am 19.04.2013 19:01, schrieb Rafa? Mi?ecki: > Some devices (ATI/AMD cards) don't support passing ELD struct to the > hardware but just require filling specific registers and then the > hardware/firmware does the rest. In such cases we need to read the info > from SAD blocks and put them in the correct registers. > > Signed-off-by: Rafa? Mi?ecki <zajec5 at gmail.com>
Maybe add some comment that the returned pointer needs to be kfreed, but apart from that it is good enough for me and: Reviewed-by: Christian K?nig <christian.koenig at amd.com> > --- > Changes since RFC: > 1) Fixed allocation (and kcalloc instead of kzalloc) > 2) Don't duplicate defines for audio codecs > 3) Some variables scope > --- > drivers/gpu/drm/drm_edid.c | 58 > ++++++++++++++++++++++++++++++++++++++++++++ > include/drm/drm_edid.h | 9 +++++++ > 2 files changed, 67 insertions(+) > > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c > index e2acfdb..89ae211 100644 > --- a/drivers/gpu/drm/drm_edid.c > +++ b/drivers/gpu/drm/drm_edid.c > @@ -2511,6 +2511,64 @@ void drm_edid_to_eld(struct drm_connector *connector, > struct edid *edid) > EXPORT_SYMBOL(drm_edid_to_eld); > > /** > + * drm_edid_to_sad - extracts SADs from EDID > + * @edid: EDID to parse > + * @sads: pointer that will be set to the extracted SADs > + * > + * Looks for CEA EDID block and extracts SADs (Short Audio Descriptors) from > it. > + * > + * Return number of found SADs or negative number on error. > + */ > +int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads) > +{ > + int count = 0; > + int i, start, end, dbl; > + u8 *cea; > + > + cea = drm_find_cea_extension(edid); > + if (!cea) { > + DRM_DEBUG_KMS("SAD: no CEA Extension found\n"); > + return -ENOENT; > + } > + > + if (cea_revision(cea) < 3) { > + DRM_DEBUG_KMS("SAD: wrong CEA revision\n"); > + return -ENOTSUPP; > + } > + > + if (cea_db_offsets(cea, &start, &end)) { > + DRM_DEBUG_KMS("SAD: invalid data block offsets\n"); > + return -EPROTO; > + } > + > + for_each_cea_db(cea, i, start, end) { > + u8 *db = &cea[i]; > + > + if (cea_db_tag(db) == AUDIO_BLOCK) { > + dbl = cea_db_payload_len(db); > + int j; > + > + count = dbl / 3; /* SAD is 3B */ > + *sads = kcalloc(count, sizeof(**sads), GFP_KERNEL); > + if (!*sads) > + return -ENOMEM; > + for (j = 0; j < count; j++) { > + u8 *sad = &db[1 + j * 3]; > + > + (*sads)[j].format = (sad[0] & 0x78) >> 3; > + (*sads)[j].channels = sad[0] & 0x7; > + (*sads)[j].freq = sad[1] & 0x7F; > + (*sads)[j].byte2 = sad[2]; > + } > + break; > + } > + } > + > + return count; > +} > +EXPORT_SYMBOL(drm_edid_to_sad); > + > +/** > * drm_av_sync_delay - HDMI/DP sink audio-video sync delay in millisecond > * @connector: connector associated with the HDMI/DP sink > * @mode: the display mode > diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h > index 5da1b4a..fc481fc 100644 > --- a/include/drm/drm_edid.h > +++ b/include/drm/drm_edid.h > @@ -244,12 +244,21 @@ struct edid { > > #define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8)) > > +/* Short Audio Descriptor */ > +struct cea_sad { > + u8 format; > + u8 channels; /* max number of channels - 1 */ > + u8 freq; > + u8 byte2; /* meaning depends on format */ > +}; > + > struct drm_encoder; > struct drm_connector; > struct drm_display_mode; > struct hdmi_avi_infoframe; > > void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid); > +int drm_edid_to_sad(struct edid *edid, struct cea_sad **sads); > int drm_av_sync_delay(struct drm_connector *connector, > struct drm_display_mode *mode); > struct drm_connector *drm_select_eld(struct drm_encoder *encoder,