static void stmhdmi_connect_display(struct stm_hdmi *hdmi) { if(!hdmi->disable) { stm_meta_data_result_t res; DPRINTK("HDMI Output connected, signalling for authentication hdmi = %p queue = %p\n",hdmi,&(hdmi->auth_wait_queue)); hdmi->auth = 0; wake_up(&(hdmi->auth_wait_queue)); /* * Schedule the sending of the product identifier HDMI info frame. */ if(hdmi->spd_frame->data[HDMI_SPD_INFOFRAME_SPI_OFFSET] != HDMI_SPD_INFOFRAME_SPI_DISABLE) (void)stm_display_output_queue_metadata(hdmi->hdmi_output, hdmi->spd_metadata, &res); /* * Signal the hotplug sysfs attribute now the output is alive. */ sysfs_notify(&(hdmi->class_device->kobj), NULL, "hotplug"); } else { DPRINTK("HDMI Output disabled\n"); stm_display_output_stop(hdmi->hdmi_output); } }
/******************************************************************************* * Kernel management thread state machine helper functions. */ static void stmhdmi_handle_wait_queue_timeout(struct stm_hdmi *hdmi) { switch(hdmi->status) { case STM_DISPLAY_CONNECTED: { stm_meta_data_result_t res; /* * We are deliberately ignoring any errors when queuing SPD data as * (a) they really shouldn't happen and (b) there is nothing we can * do about it anyway. */ if(hdmi->spd_frame->data[HDMI_SPD_INFOFRAME_SPI_OFFSET] != HDMI_SPD_INFOFRAME_SPI_DISABLE) (void)stm_display_output_queue_metadata(hdmi->hdmi_output, hdmi->spd_metadata, &res); break; } case STM_DISPLAY_DISCONNECTED: { if(hdmi->deferred_disconnect > 0) { hdmi->deferred_disconnect--; if(hdmi->deferred_disconnect == 0) { DPRINTK("Doing deferred output disable due to HPD de-assert timeout\n"); stm_display_output_stop(hdmi->hdmi_output); hdmi->disable = true; sysfs_notify(&(hdmi->class_device->kobj), NULL, "hotplug"); } } break; } default: break; } }
static int stmhdmi_set_audio_iframe_data(struct stm_hdmi *dev, unsigned long arg) { stm_meta_data_result_t res; stm_meta_data_t *metadata; stm_hdmi_info_frame_t *iframe; struct stmhdmiio_audio audiocfg; if (copy_from_user(&audiocfg,(void*)arg,sizeof(audiocfg))) return -EFAULT; if((metadata = kzalloc(sizeof(stm_meta_data_t)+sizeof(stm_hdmi_info_frame_t),GFP_KERNEL)) == 0) return -ENOMEM; metadata->size = sizeof(stm_meta_data_t)+sizeof(stm_hdmi_info_frame_t); metadata->release = (void(*)(struct stm_meta_data_s*))kfree; metadata->type = STM_METADATA_TYPE_AUDIO_IFRAME; iframe = (stm_hdmi_info_frame_t*)&metadata->data[0]; iframe->type = HDMI_AUDIO_INFOFRAME_TYPE; iframe->version = HDMI_AUDIO_INFOFRAME_VERSION; iframe->length = HDMI_AUDIO_INFOFRAME_LENGTH; iframe->data[1] = (audiocfg.channel_count << HDMI_AUDIO_INFOFRAME_CHANNEL_COUNT_SHIFT) & HDMI_AUDIO_INFOFRAME_CHANNEL_COUNT_MASK; iframe->data[2] = (audiocfg.sample_frequency << HDMI_AUDIO_INFOFRAME_FREQ_SHIFT) & HDMI_AUDIO_INFOFRAME_FREQ_MASK; iframe->data[4] = audiocfg.speaker_mapping; iframe->data[5] = audiocfg.downmix_info & (HDMI_AUDIO_INFOFRAME_LEVELSHIFT_MASK| HDMI_AUDIO_INFOFRAME_DOWNMIX_INHIBIT); if(stm_display_output_queue_metadata(dev->hdmi_output, metadata, &res)<0) { kfree(metadata); if(signal_pending(current)) return -ERESTARTSYS; else return -EIO; } return stmhdmi_convert_metadata_result_to_errno(res); }
static int stmfb_set_picture_configuration(struct stmfbio_picture_configuration *cfg, struct stmfb_info *i) { stm_meta_data_result_t res; stm_meta_data_t *metadata; stm_picture_format_info_t *pic_info; unsigned long flags; int ret = 0; if((cfg->flags & STMFBIO_PICTURE_FLAGS_PICUTRE_ASPECT) && (cfg->picture_aspect > STMFBIO_PIC_PICTURE_ASPECT_16_9)) return -EINVAL; if((cfg->flags & STMFBIO_PICTURE_FLAGS_VIDEO_ASPECT) && (cfg->video_aspect > STMFBIO_PIC_VIDEO_ASPECT_GT_16_9)) return -EINVAL; if((cfg->flags & STMFBIO_PICTURE_FLAGS_LETTERBOX) && (cfg->letterbox_style > STMFBIO_PIC_LETTERBOX_SAP_4_3)) return -EINVAL; if((cfg->flags & STMFBIO_PICTURE_FLAGS_RESCALE_INFO) && (cfg->picture_rescale > STMFBIO_PIC_RESCALE_BOTH)) return -EINVAL; if((metadata = kzalloc(sizeof(stm_meta_data_t)+sizeof(stm_picture_format_info_t),GFP_KERNEL)) == 0) return -ENOMEM; metadata->size = sizeof(stm_meta_data_t)+sizeof(stm_picture_format_info_t); metadata->type = STM_METADATA_TYPE_PICTURE_INFO; /* * We need to hold a reference for the metadata as we need to queue * the same thing twice (to analogue and HDMI outputs) and need to ensure we * do not get any race conditions in the lifetime of the object. We use the * metadata addref/release internal helpers to manage this. We must use * the release under interrupt lock once a queue has been successful to * ensure it is atomic with the VTG interrupt processing, but we actually do * this all the time to make the point and stop the code from * breaking in future changes. */ spin_lock_irqsave(&i->framebufferSpinLock,flags); stm_meta_data_addref(metadata); spin_unlock_irqrestore(&i->framebufferSpinLock,flags); metadata->release = (void(*)(struct stm_meta_data_s*))kfree; metadata->presentationTime = ((TIME64)cfg->timestamp.tv_sec * USEC_PER_SEC) + (TIME64)cfg->timestamp.tv_usec; pic_info = (stm_picture_format_info_t*)&metadata->data[0]; pic_info->flags = cfg->flags; pic_info->pictureAspect = (stm_wss_t)cfg->picture_aspect; pic_info->videoAspect = (stm_wss_t)cfg->video_aspect; pic_info->letterboxStyle = (stm_letterbox_t)cfg->letterbox_style; pic_info->pictureRescale = (stm_picture_rescale_t)cfg->picture_rescale; pic_info->barEnable = cfg->bar_enable; pic_info->topEndLine = cfg->top_bar_end; pic_info->bottomStartLine = cfg->bottom_bar_start; pic_info->leftEndPixel = cfg->left_bar_end; pic_info->rightStartPixel = cfg->right_bar_start; if(stm_display_output_queue_metadata(i->pFBMainOutput, metadata, &res)<0) { ret = signal_pending(current)?-EINTR:-EIO; goto exit; } /* * Set first stab at the wanted return value, this is likely to be revised * below. */ ret = stmfb_convert_metadata_result_to_errno(res); if(i->hdmi == NULL) { /* * Display pipeline has no HDMI transmitter so just return this result. */ goto exit; } switch(res) { case STM_METADATA_RES_OK: case STM_METADATA_RES_QUEUE_UNAVAILABLE: /* * If the DENC isn't in use the queue will not be available, * but that is fine we can continue to HDMI. */ break; default: goto exit; } if(mutex_lock_interruptible(&i->hdmi->lock)) { ret = -EINTR; goto exit; } /* * DVI or undetermined sink does not have InfoFrames. */ if(i->hdmi->edid_info.display_type != STM_DISPLAY_HDMI) goto mutex_exit; if((i->hdmi->status != STM_DISPLAY_CONNECTED) && (metadata->presentationTime == 0LL)) { /* * This is a slight of hand for the usual case that a change * is required immediately but the HDMI output is currently * stopped and will not process its metadata queues. Flush the queue * because we know this request will be the valid data as soon * as the HDMI output is started again. */ stm_display_output_flush_metadata(i->hdmi->hdmi_output,STM_METADATA_TYPE_PICTURE_INFO); } if(stm_display_output_queue_metadata(i->hdmi->hdmi_output, metadata, &res)<0) { ret = signal_pending(current)?-EINTR:-EIO; goto mutex_exit; } ret = stmfb_convert_metadata_result_to_errno(res); mutex_exit: mutex_unlock(&i->hdmi->lock); exit: spin_lock_irqsave(&i->framebufferSpinLock,flags); stm_meta_data_release(metadata); spin_unlock_irqrestore(&i->framebufferSpinLock,flags); return ret; }
static int stmhdmi_set_isrc_data(struct stm_hdmi *dev, unsigned long arg) { stm_meta_data_result_t res; stm_meta_data_t *metadata; stm_hdmi_isrc_data_t *isrc; struct stmhdmiio_isrc_data isrcdata; if (copy_from_user(&isrcdata,(void*)arg,sizeof(isrcdata))) return -EFAULT; /* * Don't allow the configuration of ISRC packets unless the * connected TV has the supports AI flag set in its EDID. */ if((dev->edid_info.display_type != STM_DISPLAY_HDMI) || (dev->edid_info.hdmi_vsdb_flags & STM_HDMI_VSDB_SUPPORTS_AI) == 0) { DPRINTK("Not Sending ISRC Datapackets, sink does not support AI\n"); return -EPERM; } if((metadata = kzalloc(sizeof(stm_meta_data_t)+sizeof(stm_hdmi_isrc_data_t),GFP_KERNEL)) == 0) return -ENOMEM; metadata->size = sizeof(stm_meta_data_t)+sizeof(stm_hdmi_isrc_data_t); metadata->release = (void(*)(struct stm_meta_data_s*))kfree; metadata->type = STM_METADATA_TYPE_ISRC_DATA; metadata->presentationTime = ((TIME64)isrcdata.timestamp.tv_sec * USEC_PER_SEC) + (TIME64)isrcdata.timestamp.tv_usec; isrc = (stm_hdmi_isrc_data_t*)&metadata->data[0]; isrc->isrc1.type = HDMI_ISRC1_PACKET_TYPE; isrc->isrc2.type = HDMI_ISRC2_PACKET_TYPE; if(isrcdata.status != ISRC_STATUS_DISABLE) { int i; isrc->isrc1.version = (isrcdata.status & HDMI_ISRC1_STATUS_MASK) | HDMI_ISRC1_VALID; /* * Just copy the first 16 bytes of information to ISRC1 */ memcpy(isrc->isrc1.data,&isrcdata.upc_ean_isrc[0],16); /* * For the second 16 bytes we need to see if there is any non-zero data in * there. If not then the second ISRC packet will not be transmitted. */ for(i=16;i<32;i++) { if(isrcdata.upc_ean_isrc[i] != 0) { isrc->isrc1.version |= HDMI_ISRC1_CONTINUED; isrc->isrc2.data[i-16] = isrcdata.upc_ean_isrc[i]; } } } DPRINTK("Sending ISRC Datapackets\n"); if(stm_display_output_queue_metadata(dev->hdmi_output, metadata, &res)<0) { kfree(metadata); if(signal_pending(current)) return -ERESTARTSYS; else return -EIO; } return stmhdmi_convert_metadata_result_to_errno(res); }
static int stmhdmi_send_data_packet(struct stm_hdmi *dev, unsigned long arg) { stm_meta_data_result_t res; stm_meta_data_t *metadata; stm_hdmi_info_frame_t *iframe; struct stmhdmiio_data_packet packet; if (copy_from_user(&packet,(void*)arg,sizeof(packet))) return -EFAULT; if((metadata = kzalloc(sizeof(stm_meta_data_t)+sizeof(stm_hdmi_info_frame_t),GFP_KERNEL)) == 0) return -ENOMEM; metadata->size = sizeof(stm_meta_data_t)+sizeof(stm_hdmi_info_frame_t); metadata->release = (void(*)(struct stm_meta_data_s*))kfree; metadata->presentationTime = ((TIME64)packet.timestamp.tv_sec * USEC_PER_SEC) + (TIME64)packet.timestamp.tv_usec; switch(packet.type) { case HDMI_ACP_PACKET_TYPE: { /* * Don't allow the configuration of ACP packets unless the * connected TV has the supports AI flag set in its EDID. */ if((dev->edid_info.display_type != STM_DISPLAY_HDMI) || (dev->edid_info.hdmi_vsdb_flags & STM_HDMI_VSDB_SUPPORTS_AI) == 0) { DPRINTK("Not Sending ACP Datapacket, sink does not support AI\n"); kfree(metadata); return -EPERM; } DPRINTK("Sending ACP Datapacket\n"); metadata->type = STM_METADATA_TYPE_ACP_DATA; break; } case HDMI_VENDOR_INFOFRAME_TYPE: { DPRINTK("Sending vendor IFrame\n"); metadata->type = STM_METADATA_TYPE_VENDOR_IFRAME; break; } case HDMI_NTSC_INFOFRAME_TYPE: { DPRINTK("Sending NTSC IFrame\n"); metadata->type = STM_METADATA_TYPE_NTSC_IFRAME; break; } case HDMI_GAMUT_DATA_PACKET_TYPE: { DPRINTK("Sending Color Gamut Datapacket\n"); metadata->type = STM_METADATA_TYPE_COLOR_GAMUT_DATA; break; } default: { DPRINTK("Unsupported Datapacket\n"); kfree(metadata); return -EINVAL; } } iframe = (stm_hdmi_info_frame_t*)&metadata->data[0]; iframe->type = packet.type; iframe->version = packet.version; iframe->length = packet.length; /* * Note: we cannot use packet.length to size the memcpy as this is only * valid for real InfoFrames not arbitrary HDMI data island packets. */ memcpy(&iframe->data[0],&packet.data[0],28); if(stm_display_output_queue_metadata(dev->hdmi_output, metadata, &res)<0) { kfree(metadata); if(signal_pending(current)) return -ERESTARTSYS; else return -EIO; } return stmhdmi_convert_metadata_result_to_errno(res); }