blob: 87fd2a7a1cb9d8497c7f12e0fe7505183cd9010c [file] [log] [blame]
/*
* Copyright (C) 2021 Amlogic Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdint.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/time.h>
//#include</linux/amlogic/cec_common.h>
#include "hdmi_tx_cec_20.h"
#include "data.h"
#include "audio_if.h"
#define DEV_CEC "/dev/cec"
static int Cec_fd = 0;
#define HDR 0
#define OPCODE 1
#define OPERAND 2
#define CEC_MSG_HDR_BROADCAST 0x0F
#define CEC_MSG_HDR 0x05
#define INITIATE_ARC 0XC0
#define REPORT_ARC_INITIATED 0XC1
#define REPORT_ARC_TERMINATED 0XC2
#define REQUEST_ARC_INITIATION 0XC3
#define REQUEST_ARC_TERMINATION 0XC4
#define TERMINATE_ARC 0XC5
char CEC_MSG_ARC_INITIATED[]={CEC_MSG_HDR,REPORT_ARC_INITIATED};
static pthread_t CEC_Event_Thread;
struct pollfd polling;
static bool arc_not_initialised = true;
static char cecmsg[16] ;
static int stop =0;
static char msg_to_send[16];
/* Audio stuffs */
#define TV_AUDIO_OUTPUT
#ifdef TV_AUDIO_OUTPUT
static struct audio_port_config source_;
static struct audio_port_config sink_;
static audio_patch_handle_t patch_h_;
static void test_patch(audio_hw_device_t *device)
{
int ret;
/* create mixer to device patch */
source_.id = 1;
source_.role = AUDIO_PORT_ROLE_SOURCE;
source_.type = AUDIO_PORT_TYPE_MIX;
source_.config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE |
AUDIO_PORT_CONFIG_FORMAT;
source_.sample_rate = 48000;
source_.format = AUDIO_FORMAT_PCM_16_BIT;
sink_.id = 2;
sink_.role = AUDIO_PORT_ROLE_SINK;
sink_.type = AUDIO_PORT_TYPE_DEVICE;
sink_.config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE |
AUDIO_PORT_CONFIG_FORMAT;
sink_.sample_rate = 48000;
sink_.format = AUDIO_FORMAT_PCM_16_BIT;
sink_.ext.device.type = AUDIO_DEVICE_OUT_HDMI_ARC;
printf("create mix --> ARC patch...");
ret = device->create_audio_patch(device, 1, &source_, 1, &sink_, &patch_h_);
if (ret) {
printf("fail ret:%d\n",ret);
} else {
printf("success\n");
}
}
static void destroy_patch(audio_hw_device_t *device)
{
if (patch_h_) {
int ret;
printf("destroy patch...");
ret = device->release_audio_patch(device, patch_h_);
if (ret) {
printf("fail ret:%d\n",ret);
} else {
printf("success\n");
}
}
}
#endif
static void test_vol(audio_hw_device_t *device, int gain)
{
int ret;
float vol = 0;
ret = device->get_master_volume(device, &vol);
if (ret) {
printf("get_master_volume fail\n");
} else {
printf("cur master vol:%f\n", vol);
}
ret = device->set_master_volume(device, 0.5f);
if (ret) {
printf("set_master_volume fail\n");
} else {
printf("set master vol 0.5\n");
device->set_master_volume(device, vol);
}
#ifdef TV_AUDIO_OUTPUT
{
struct audio_port_config config;
memset(&config, 0, sizeof(config));
config.id = 2;
config.role = AUDIO_PORT_ROLE_SINK;
config.type = AUDIO_PORT_TYPE_DEVICE;
config.config_mask = AUDIO_PORT_CONFIG_GAIN;
/* audio_hal use dB * 100 to keep the accuracy */
config.gain.values[0] = gain * 100;
printf("set gain to %ddB...\n", gain);
ret = device->set_audio_port_config(device, &config);
if (ret) {
printf("fail\n");
} else {
printf("success\n");
}
}
#endif
}
#define WRITE_UNIT 4096
#define min(a, b) ((a) < (b) ? (a) : (b))
static int test_stream(struct audio_stream_out *stream)
{
int i, ret;
int len;
unsigned char *data;
ret = stream->set_volume(stream, 1.0f, 1.0f);
if (ret) {
fprintf(stderr, "%s %d, ret:%x\n", __func__, __LINE__, ret);
return -1;
}
printf("Start output to audio HAL\n");
for (i = 0; i < 10; i++) {
data = wav_data;
len = sizeof(wav_data);
while (len > 0) {
ssize_t s = stream->write(stream, data, min(len, WRITE_UNIT));
if (s < 0) {
fprintf(stderr, "stream writing error %d\n", s);
break;
}
len -= s;
data += s;
}
}
return 0;
}
static void test_output_stream(audio_hw_device_t *device)
{
int ret;
struct audio_config config;
struct audio_stream_out *stream;
char cmd[32];
memset(&config, 0, sizeof(config));
config.sample_rate = 48000;
config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
config.format = AUDIO_FORMAT_PCM_16_BIT;
printf("Tell audio HAL HDMI ARC is connected\n");
ret = device->set_parameters(device, "connect=0x40000");
if (ret) {
fprintf(stderr, "set_parameters HDMI connected failed\n");
}
/*
* The following set_parameters calling sequence is needed
* to meet the audio HAL internal checking for ARC output:
* 1. "HDMI ARC Switch" key is used for UI control switch to enable
* ARC output.
* 2. "connect" key is used to tell audio HAL the connection status
*/
printf("Report UI settings for HDMI ARC enabled\n");
ret = device->set_parameters(device, "HDMI ARC Switch=1");
if (ret) {
fprintf(stderr, "set_parameters failed\n");
}
printf("Report HDMI ARC is connected\n");
snprintf(cmd, sizeof(cmd), "connect=%d", AUDIO_DEVICE_OUT_HDMI_ARC);
ret = device->set_parameters(device, cmd);
if (ret) {
fprintf(stderr, "set_parameters failed\n");
}
printf("open output to HDMI_ARC with direct mode...\n");
ret = device->open_output_stream(device,
0, AUDIO_DEVICE_OUT_HDMI_ARC,
AUDIO_OUTPUT_FLAG_PRIMARY | AUDIO_OUTPUT_FLAG_DIRECT, &config,
&stream, NULL);
if (ret) {
printf("fail\n");
return;
} else {
printf("success\n");
}
test_stream(stream);
printf("close output speaker...\n");
device->close_output_stream(device, stream);
}
void process_cec_msg(char * cec_msg,int len)
{
int i=0;
switch (cec_msg[OPCODE]) {
case INITIATE_ARC :
printf("Got the Initiate ARC message Amplifeir requesting to start ARC\n");
arc_not_initialised = false;
write(Cec_fd,CEC_MSG_ARC_INITIATED,2);
printf("Sending the ARC initiated message to the Amplifier\n");
stop =1;
break;
case CEC_OC_GIVE_PHYSICAL_ADDRESS :
printf("CEC_OC_GIVE_PHYSICAL_ADDRESS\n");
msg_to_send[0] = CEC_MSG_HDR_BROADCAST;
msg_to_send[1] = CEC_OC_REPORT_PHYSICAL_ADDRESS;
msg_to_send[2] = 0x00;
msg_to_send[3] = 0x00;
msg_to_send[4] = 0x00;
write(Cec_fd,msg_to_send,5);
printf("Sending CEC_OC_REPORT_PHYSICAL_ADDRESS\n");
break;
default:
{
printf("CEC msg is ");
for(i=0;i< len;i++) {
printf(" 0x%x ",cec_msg[i]);
}
}
printf("\n###################");
break;
}
}
void* CEC_event_read_fn (void *data)
{
int timeout = -1;
unsigned int mask = 0;
int read_len =0;
polling.fd= Cec_fd;
polling.events = POLLIN;
while (!stop) {
mask = poll(&polling,1,timeout);
if (mask & POLLIN || mask & POLLRDNORM) {
read_len= read(Cec_fd, &cecmsg, 1);
process_cec_msg(cecmsg,read_len);
}
}
return NULL;
}
static void sighandler(int sig)
{
printf("Interrupted by signal %d\n", sig);
stop= 1;
printf("sighandler! Done\n");
}
int main(int argc, char *argv[])
{
char msg[16];
int ret=0;
audio_hw_device_t *device;
int test_earc = 0;
printf("Usage:\n");
printf(" test_arc : start ARC handshaking with CEC and then play a tone.\n");
printf(" test_arc 1 : set parameter 1 to do eARC playback w/o CEC communications.\n");
printf(" Output format can be set by digital_mode command.\n");
if (argc > 1) {
test_earc = atoi(argv[1]);
if (test_earc) {
printf("Test EARC output, CEC communicatin will be skipped\n");
}
}
if (!test_earc) {
Cec_fd = open (DEV_CEC, O_RDWR);
if (Cec_fd < 0) {
printf ("%s CEC_device opening returned %d", DEV_CEC, Cec_fd);
return -1;
}
ret = pthread_create(&CEC_Event_Thread, NULL, CEC_event_read_fn, NULL);
if (ret) {
printf("Unable to create decoder event thread\n");
}
/* setup the signal handler for CTRL-C */
signal(SIGINT, sighandler);
/*kill -USR1 pid */
signal(SIGUSR1, sighandler);
ioctl(Cec_fd, CEC_IOC_SET_OPTION_SYS_CTRL, 0x8);
ioctl(Cec_fd, CEC_IOC_ADD_LOGICAL_ADDR, 0x0);
msg[HDR]=CEC_MSG_HDR;
msg[OPCODE]=REQUEST_ARC_INITIATION;
write(Cec_fd,msg,2);
printf("sending ARC initialisation \n");
}
ret = audio_hw_load_interface(&device);
if (ret) {
printf("%s %d error:%d\n", __func__, __LINE__, ret);
return ret;
}
printf("hw version: %x\n", device->common.version);
printf("hal api version: %x\n", device->common.module->hal_api_version);
printf("module id: %s\n", device->common.module->id);
printf("module name: %s\n", device->common.module->name);
if (device->get_supported_devices) {
uint32_t support_dev = 0;
support_dev = device->get_supported_devices(device);
printf("supported device: %x\n", support_dev);
}
int inited = device->init_check(device);
if (inited) {
printf("device not inited, quit\n");
goto exit;
}
if (!test_earc) {
while (!stop) {
if (arc_not_initialised) {
printf("TV sending Request ARC initialisation to Amplifier \n");
write(Cec_fd,msg,2);
}
sleep(1);
}
}
sleep(2);
#ifdef TV_AUDIO_OUTPUT
test_patch(device);
#endif
test_output_stream(device);
if (!test_earc) {
ret = pthread_join(CEC_Event_Thread, NULL);
if (ret != 0) {
printf("CEC_Event_Thread returned error\n");
}
ioctl(Cec_fd, CEC_IOC_CLR_LOGICAL_ADDR, 0x0);
ioctl(Cec_fd, CEC_IOC_SET_OPTION_SYS_CTRL, 0x0);
close(Cec_fd);
}
/* Audio clean up */
#ifdef TV_AUDIO_OUTPUT
destroy_patch(device);
#endif
exit:
device->common.close(&device->common);
audio_hw_unload_interface(device);
return 0;
}