| /* |
| * 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;
|
|
|
| }
|
|
|
|
|
|
|