brintos

brintos / linux-shallow public Read only

0
0
Text · 17.3 KiB · a047ed0 Raw
590 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Driver for Digigram miXart soundcards4 *5 * low level interface with interrupt handling and mail box implementation6 *7 * Copyright (c) 2003 by Digigram <alsa@digigram.com>8 */9 10#include <linux/interrupt.h>11#include <linux/mutex.h>12#include <linux/pci.h>13#include <linux/io.h>14 15#include <sound/core.h>16#include "mixart.h"17#include "mixart_hwdep.h"18#include "mixart_core.h"19 20 21#define MSG_TIMEOUT_JIFFIES         (400 * HZ) / 1000 /* 400 ms */22 23#define MSG_DESCRIPTOR_SIZE         0x2424#define MSG_HEADER_SIZE             (MSG_DESCRIPTOR_SIZE + 4)25 26#define MSG_TYPE_MASK               0x00000003    /* mask for following types */27#define MSG_TYPE_NOTIFY             0             /* embedded -> driver (only notification, do not get_msg() !) */28#define MSG_TYPE_COMMAND            1             /* driver <-> embedded (a command has no answer) */29#define MSG_TYPE_REQUEST            2             /* driver -> embedded (request will get an answer back) */30#define MSG_TYPE_ANSWER             3             /* embedded -> driver */31#define MSG_CANCEL_NOTIFY_MASK      0x80000000    /* this bit is set for a notification that has been canceled */32 33 34static int retrieve_msg_frame(struct mixart_mgr *mgr, u32 *msg_frame)35{36	/* read the message frame fifo */37	u32 headptr, tailptr;38 39	tailptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL));40	headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_HEAD));41 42	if (tailptr == headptr)43		return 0; /* no message posted */44 45	if (tailptr < MSG_OUTBOUND_POST_STACK)46		return 0; /* error */47	if (tailptr >= MSG_OUTBOUND_POST_STACK + MSG_BOUND_STACK_SIZE)48		return 0; /* error */49 50	*msg_frame = readl_be(MIXART_MEM(mgr, tailptr));51 52	/* increment the tail index */53	tailptr += 4;54	if( tailptr >= (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) )55		tailptr = MSG_OUTBOUND_POST_STACK;56	writel_be(tailptr, MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL));57 58	return 1;59}60 61static int get_msg(struct mixart_mgr *mgr, struct mixart_msg *resp,62		   u32 msg_frame_address )63{64	u32  headptr;65	u32  size;66	int  err;67#ifndef __BIG_ENDIAN68	unsigned int i;69#endif70 71	err = 0;72 73	/* copy message descriptor from miXart to driver */74	size                =  readl_be(MIXART_MEM(mgr, msg_frame_address));       /* size of descriptor + response */75	resp->message_id    =  readl_be(MIXART_MEM(mgr, msg_frame_address + 4));   /* dwMessageID */76	resp->uid.object_id =  readl_be(MIXART_MEM(mgr, msg_frame_address + 8));   /* uidDest */77	resp->uid.desc      =  readl_be(MIXART_MEM(mgr, msg_frame_address + 12));  /* */78 79	if( (size < MSG_DESCRIPTOR_SIZE) || (resp->size < (size - MSG_DESCRIPTOR_SIZE))) {80		err = -EINVAL;81		dev_err(&mgr->pci->dev,82			"problem with response size = %d\n", size);83		goto _clean_exit;84	}85	size -= MSG_DESCRIPTOR_SIZE;86 87	memcpy_fromio(resp->data, MIXART_MEM(mgr, msg_frame_address + MSG_HEADER_SIZE ), size);88	resp->size = size;89 90	/* swap if necessary */91#ifndef __BIG_ENDIAN92	size /= 4; /* u32 size */93	for(i=0; i < size; i++) {94		((u32*)resp->data)[i] = be32_to_cpu(((__be32*)resp->data)[i]);95	}96#endif97 98	/*99	 * free message frame address100	 */101	headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD));102 103	if( (headptr < MSG_OUTBOUND_FREE_STACK) || ( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) {104		err = -EINVAL;105		goto _clean_exit;106	}107 108	/* give address back to outbound fifo */109	writel_be(msg_frame_address, MIXART_MEM(mgr, headptr));110 111	/* increment the outbound free head */112	headptr += 4;113	if( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) )114		headptr = MSG_OUTBOUND_FREE_STACK;115 116	writel_be(headptr, MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD));117 118 _clean_exit:119	return err;120}121 122 123/*124 * send a message to miXart. return: the msg_frame used for this message125 */126/* call with mgr->msg_lock held! */127static int send_msg( struct mixart_mgr *mgr,128		     struct mixart_msg *msg,129		     int max_answersize,130		     int mark_pending,131		     u32 *msg_event)132{133	u32 headptr, tailptr;134	u32 msg_frame_address;135	int i;136 137	if (snd_BUG_ON(msg->size % 4))138		return -EINVAL;139 140	/* get message frame address */141	tailptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL));142	headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_HEAD));143 144	if (tailptr == headptr) {145		dev_err(&mgr->pci->dev, "error: no message frame available\n");146		return -EBUSY;147	}148 149	if( (tailptr < MSG_INBOUND_FREE_STACK) || (tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) {150		return -EINVAL;151	}152 153	msg_frame_address = readl_be(MIXART_MEM(mgr, tailptr));154	writel(0, MIXART_MEM(mgr, tailptr)); /* set address to zero on this fifo position */155 156	/* increment the inbound free tail */157	tailptr += 4;158	if( tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) )159		tailptr = MSG_INBOUND_FREE_STACK;160 161	writel_be(tailptr, MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL));162 163	/* TODO : use memcpy_toio() with intermediate buffer to copy the message */164 165	/* copy message descriptor to card memory */166	writel_be( msg->size + MSG_DESCRIPTOR_SIZE,      MIXART_MEM(mgr, msg_frame_address) );      /* size of descriptor + request */167	writel_be( msg->message_id ,                     MIXART_MEM(mgr, msg_frame_address + 4) );  /* dwMessageID */168	writel_be( msg->uid.object_id,                   MIXART_MEM(mgr, msg_frame_address + 8) );  /* uidDest */169	writel_be( msg->uid.desc,                        MIXART_MEM(mgr, msg_frame_address + 12) ); /* */170	writel_be( MSG_DESCRIPTOR_SIZE,                  MIXART_MEM(mgr, msg_frame_address + 16) ); /* SizeHeader */171	writel_be( MSG_DESCRIPTOR_SIZE,                  MIXART_MEM(mgr, msg_frame_address + 20) ); /* OffsetDLL_T16 */172	writel_be( msg->size,                            MIXART_MEM(mgr, msg_frame_address + 24) ); /* SizeDLL_T16 */173	writel_be( MSG_DESCRIPTOR_SIZE,                  MIXART_MEM(mgr, msg_frame_address + 28) ); /* OffsetDLL_DRV */174	writel_be( 0,                                    MIXART_MEM(mgr, msg_frame_address + 32) ); /* SizeDLL_DRV */175	writel_be( MSG_DESCRIPTOR_SIZE + max_answersize, MIXART_MEM(mgr, msg_frame_address + 36) ); /* dwExpectedAnswerSize */176 177	/* copy message data to card memory */178	for( i=0; i < msg->size; i+=4 ) {179		writel_be( *(u32*)(msg->data + i), MIXART_MEM(mgr, MSG_HEADER_SIZE + msg_frame_address + i)  );180	}181 182	if( mark_pending ) {183		if( *msg_event ) {184			/* the pending event is the notification we wait for ! */185			mgr->pending_event = *msg_event;186		}187		else {188			/* the pending event is the answer we wait for (same address than the request)! */189			mgr->pending_event = msg_frame_address;190 191			/* copy address back to caller */192			*msg_event = msg_frame_address;193		}194	}195 196	/* mark the frame as a request (will have an answer) */197	msg_frame_address |= MSG_TYPE_REQUEST;198 199	/* post the frame */200	headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD));201 202	if( (headptr < MSG_INBOUND_POST_STACK) || (headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE))) {203		return -EINVAL;204	}205 206	writel_be(msg_frame_address, MIXART_MEM(mgr, headptr));207 208	/* increment the inbound post head */209	headptr += 4;210	if( headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) )211		headptr = MSG_INBOUND_POST_STACK;212 213	writel_be(headptr, MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD));214 215	return 0;216}217 218 219int snd_mixart_send_msg(struct mixart_mgr *mgr, struct mixart_msg *request, int max_resp_size, void *resp_data)220{221	struct mixart_msg resp;222	u32 msg_frame = 0; /* set to 0, so it's no notification to wait for, but the answer */223	int err;224	wait_queue_entry_t wait;225	long timeout;226 227	init_waitqueue_entry(&wait, current);228 229	mutex_lock(&mgr->msg_lock);230	/* send the message */231	err = send_msg(mgr, request, max_resp_size, 1, &msg_frame);  /* send and mark the answer pending */232	if (err) {233		mutex_unlock(&mgr->msg_lock);234		return err;235	}236 237	set_current_state(TASK_UNINTERRUPTIBLE);238	add_wait_queue(&mgr->msg_sleep, &wait);239	mutex_unlock(&mgr->msg_lock);240	timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);241	remove_wait_queue(&mgr->msg_sleep, &wait);242 243	if (! timeout) {244		/* error - no ack */245		dev_err(&mgr->pci->dev,246			"error: no response on msg %x\n", msg_frame);247		return -EIO;248	}249 250	/* retrieve the answer into the same struct mixart_msg */251	resp.message_id = 0;252	resp.uid = (struct mixart_uid){0,0};253	resp.data = resp_data;254	resp.size = max_resp_size;255 256	mutex_lock(&mgr->msg_lock);257	err = get_msg(mgr, &resp, msg_frame);258	mutex_unlock(&mgr->msg_lock);259 260	if( request->message_id != resp.message_id )261		dev_err(&mgr->pci->dev, "RESPONSE ERROR!\n");262 263	return err;264}265 266 267int snd_mixart_send_msg_wait_notif(struct mixart_mgr *mgr,268				   struct mixart_msg *request, u32 notif_event)269{270	int err;271	wait_queue_entry_t wait;272	long timeout;273 274	if (snd_BUG_ON(!notif_event))275		return -EINVAL;276	if (snd_BUG_ON((notif_event & MSG_TYPE_MASK) != MSG_TYPE_NOTIFY))277		return -EINVAL;278	if (snd_BUG_ON(notif_event & MSG_CANCEL_NOTIFY_MASK))279		return -EINVAL;280 281	init_waitqueue_entry(&wait, current);282 283	mutex_lock(&mgr->msg_lock);284	/* send the message */285	err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 1, &notif_event);  /* send and mark the notification event pending */286	if(err) {287		mutex_unlock(&mgr->msg_lock);288		return err;289	}290 291	set_current_state(TASK_UNINTERRUPTIBLE);292	add_wait_queue(&mgr->msg_sleep, &wait);293	mutex_unlock(&mgr->msg_lock);294	timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);295	remove_wait_queue(&mgr->msg_sleep, &wait);296 297	if (! timeout) {298		/* error - no ack */299		dev_err(&mgr->pci->dev,300			"error: notification %x not received\n", notif_event);301		return -EIO;302	}303 304	return 0;305}306 307 308int snd_mixart_send_msg_nonblock(struct mixart_mgr *mgr, struct mixart_msg *request)309{310	u32 message_frame;311	int err;312 313	/* just send the message (do not mark it as a pending one) */314	mutex_lock(&mgr->msg_lock);315	err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 0, &message_frame);316	mutex_unlock(&mgr->msg_lock);317 318	/* the answer will be handled by snd_struct mixart_msgasklet()  */319	atomic_inc(&mgr->msg_processed);320 321	return err;322}323 324 325/* common buffer of interrupt to send/receive messages */326static u32 mixart_msg_data[MSG_DEFAULT_SIZE / 4];327 328 329static void snd_mixart_process_msg(struct mixart_mgr *mgr)330{331	struct mixart_msg resp;332	u32 msg, addr, type;333	int err;334 335	while (mgr->msg_fifo_readptr != mgr->msg_fifo_writeptr) {336		msg = mgr->msg_fifo[mgr->msg_fifo_readptr];337		mgr->msg_fifo_readptr++;338		mgr->msg_fifo_readptr %= MSG_FIFO_SIZE;339 340		/* process the message ... */341		addr = msg & ~MSG_TYPE_MASK;342		type = msg & MSG_TYPE_MASK;343 344		switch (type) {345		case MSG_TYPE_ANSWER:346			/* answer to a message on that we did not wait for (send_msg_nonblock) */347			resp.message_id = 0;348			resp.data = mixart_msg_data;349			resp.size = sizeof(mixart_msg_data);350			err = get_msg(mgr, &resp, addr);351			if( err < 0 ) {352				dev_err(&mgr->pci->dev,353					"error(%d) reading mf %x\n",354					err, msg);355				break;356			}357 358			switch(resp.message_id) {359			case MSG_STREAM_START_INPUT_STAGE_PACKET:360			case MSG_STREAM_START_OUTPUT_STAGE_PACKET:361			case MSG_STREAM_STOP_INPUT_STAGE_PACKET:362			case MSG_STREAM_STOP_OUTPUT_STAGE_PACKET:363				if(mixart_msg_data[0])364					dev_err(&mgr->pci->dev,365						"error MSG_STREAM_ST***_***PUT_STAGE_PACKET status=%x\n",366						mixart_msg_data[0]);367				break;368			default:369				dev_dbg(&mgr->pci->dev,370					"received mf(%x) : msg_id(%x) uid(%x, %x) size(%zd)\n",371					   msg, resp.message_id, resp.uid.object_id, resp.uid.desc, resp.size);372				break;373			}374			break;375 		case MSG_TYPE_NOTIFY:376			/* msg contains no address ! do not get_msg() ! */377		case MSG_TYPE_COMMAND:378			/* get_msg() necessary */379		default:380			dev_err(&mgr->pci->dev,381				"doesn't know what to do with message %x\n",382				msg);383		} /* switch type */384 385		/* decrement counter */386		atomic_dec(&mgr->msg_processed);387 388	} /* while there is a msg in fifo */389}390 391 392irqreturn_t snd_mixart_interrupt(int irq, void *dev_id)393{394	struct mixart_mgr *mgr = dev_id;395	u32 it_reg;396 397	it_reg = readl_le(MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET));398	if( !(it_reg & MIXART_OIDI) ) {399		/* this device did not cause the interrupt */400		return IRQ_NONE;401	}402 403	/* mask all interrupts */404	writel_le(MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG(mgr, MIXART_PCI_OMIMR_OFFSET));405 406	/* outdoorbell register clear */407	it_reg = readl(MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET));408	writel(it_reg, MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET));409 410	/* clear interrupt */411	writel_le( MIXART_OIDI, MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET) );412 413	return IRQ_WAKE_THREAD;414}415 416irqreturn_t snd_mixart_threaded_irq(int irq, void *dev_id)417{418	struct mixart_mgr *mgr = dev_id;419	int err;420	struct mixart_msg resp;421	u32 msg;422 423	mutex_lock(&mgr->lock);424	/* process interrupt */425	while (retrieve_msg_frame(mgr, &msg)) {426 427		switch (msg & MSG_TYPE_MASK) {428		case MSG_TYPE_COMMAND:429			resp.message_id = 0;430			resp.data = mixart_msg_data;431			resp.size = sizeof(mixart_msg_data);432			err = get_msg(mgr, &resp, msg & ~MSG_TYPE_MASK);433			if( err < 0 ) {434				dev_err(&mgr->pci->dev,435					"interrupt: error(%d) reading mf %x\n",436					err, msg);437				break;438			}439 440			if(resp.message_id == MSG_SERVICES_TIMER_NOTIFY) {441				int i;442				struct mixart_timer_notify *notify;443				notify = (struct mixart_timer_notify *)mixart_msg_data;444 445				BUILD_BUG_ON(sizeof(notify) > sizeof(mixart_msg_data));446				if (snd_BUG_ON(notify->stream_count > ARRAY_SIZE(notify->streams)))447					break;448				for(i=0; i<notify->stream_count; i++) {449 450					u32 buffer_id = notify->streams[i].buffer_id;451					unsigned int chip_number =  (buffer_id & MIXART_NOTIFY_CARD_MASK) >> MIXART_NOTIFY_CARD_OFFSET; /* card0 to 3 */452					unsigned int pcm_number  =  (buffer_id & MIXART_NOTIFY_PCM_MASK ) >> MIXART_NOTIFY_PCM_OFFSET;  /* pcm0 to 3  */453					unsigned int sub_number  =   buffer_id & MIXART_NOTIFY_SUBS_MASK;             /* 0 to MIXART_PLAYBACK_STREAMS */454					unsigned int is_capture  = ((buffer_id & MIXART_NOTIFY_CAPT_MASK) != 0);      /* playback == 0 / capture == 1 */455 456					struct snd_mixart *chip  = mgr->chip[chip_number];457					struct mixart_stream *stream;458 459					if ((chip_number >= mgr->num_cards) || (pcm_number >= MIXART_PCM_TOTAL) || (sub_number >= MIXART_PLAYBACK_STREAMS)) {460						dev_err(&mgr->pci->dev,461							"error MSG_SERVICES_TIMER_NOTIFY buffer_id (%x) pos(%d)\n",462							   buffer_id, notify->streams[i].sample_pos_low_part);463						break;464					}465 466					if (is_capture)467						stream = &chip->capture_stream[pcm_number];468					else469						stream = &chip->playback_stream[pcm_number][sub_number];470 471					if (stream->substream && (stream->status == MIXART_STREAM_STATUS_RUNNING)) {472						struct snd_pcm_runtime *runtime = stream->substream->runtime;473						int elapsed = 0;474						u64 sample_count = ((u64)notify->streams[i].sample_pos_high_part) << 32;475						sample_count |= notify->streams[i].sample_pos_low_part;476 477						while (1) {478							u64 new_elapse_pos = stream->abs_period_elapsed +  runtime->period_size;479 480							if (new_elapse_pos > sample_count) {481								break; /* while */482							}483							else {484								elapsed = 1;485								stream->buf_periods++;486								if (stream->buf_periods >= runtime->periods)487									stream->buf_periods = 0;488 489								stream->abs_period_elapsed = new_elapse_pos;490							}491						}492						stream->buf_period_frag = (u32)( sample_count - stream->abs_period_elapsed );493 494						if(elapsed) {495							mutex_unlock(&mgr->lock);496							snd_pcm_period_elapsed(stream->substream);497							mutex_lock(&mgr->lock);498						}499					}500				}501				break;502			}503			if(resp.message_id == MSG_SERVICES_REPORT_TRACES) {504				if(resp.size > 1) {505#ifndef __BIG_ENDIAN506					/* Traces are text: the swapped msg_data has to be swapped back ! */507					int i;508					for(i=0; i<(resp.size/4); i++) {509						((__be32*)mixart_msg_data)[i] = cpu_to_be32((mixart_msg_data)[i]);510					}511#endif512					((char*)mixart_msg_data)[resp.size - 1] = 0;513					dev_dbg(&mgr->pci->dev,514						"MIXART TRACE : %s\n",515						(char *)mixart_msg_data);516				}517				break;518			}519 520			dev_dbg(&mgr->pci->dev, "command %x not handled\n",521				resp.message_id);522			break;523 524		case MSG_TYPE_NOTIFY:525			if(msg & MSG_CANCEL_NOTIFY_MASK) {526				msg &= ~MSG_CANCEL_NOTIFY_MASK;527				dev_err(&mgr->pci->dev,528					"canceled notification %x !\n", msg);529			}530			fallthrough;531		case MSG_TYPE_ANSWER:532			/* answer or notification to a message we are waiting for*/533			mutex_lock(&mgr->msg_lock);534			if( (msg & ~MSG_TYPE_MASK) == mgr->pending_event ) {535				wake_up(&mgr->msg_sleep);536				mgr->pending_event = 0;537			}538			/* answer to a message we did't want to wait for */539			else {540				mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg;541				mgr->msg_fifo_writeptr++;542				mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE;543				snd_mixart_process_msg(mgr);544			}545			mutex_unlock(&mgr->msg_lock);546			break;547		case MSG_TYPE_REQUEST:548		default:549			dev_dbg(&mgr->pci->dev,550				"interrupt received request %x\n", msg);551			/* TODO : are there things to do here ? */552			break;553		} /* switch on msg type */554	} /* while there are msgs */555 556	/* allow interrupt again */557	writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));558 559	mutex_unlock(&mgr->lock);560 561	return IRQ_HANDLED;562}563 564 565void snd_mixart_init_mailbox(struct mixart_mgr *mgr)566{567	writel( 0, MIXART_MEM( mgr, MSG_HOST_RSC_PROTECTION ) );568	writel( 0, MIXART_MEM( mgr, MSG_AGENT_RSC_PROTECTION ) );569 570	/* allow outbound messagebox to generate interrupts */571	if(mgr->irq >= 0) {572		writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));573	}574	return;575}576 577void snd_mixart_exit_mailbox(struct mixart_mgr *mgr)578{579	/* no more interrupts on outbound messagebox */580	writel_le( MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));581	return;582}583 584void snd_mixart_reset_board(struct mixart_mgr *mgr)585{586	/* reset miXart */587	writel_be( 1, MIXART_REG(mgr, MIXART_BA1_BRUTAL_RESET_OFFSET) );588	return;589}590